mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 07:06:11 -04:00
Merge branch 'google3'
Change-Id: I674779bb692da84fd8718bb59ffa3eecf9011a3f
This commit is contained in:
+43
-24
@@ -1,24 +1,29 @@
|
||||
cc_library(
|
||||
name = "core",
|
||||
hdrs = [
|
||||
srcs = [
|
||||
"core.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"core.h",
|
||||
],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__pkg__",
|
||||
"//location/nearby/setup/core/internal:__pkg__",
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":types",
|
||||
":core_types",
|
||||
"//core/internal",
|
||||
"//platform:types",
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//absl/strings",
|
||||
"//absl/time",
|
||||
"//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
name = "core_types",
|
||||
srcs = [
|
||||
"payload.cc",
|
||||
"strategy.cc",
|
||||
],
|
||||
hdrs = [
|
||||
@@ -30,29 +35,43 @@ cc_library(
|
||||
"strategy.h",
|
||||
],
|
||||
visibility = [
|
||||
"//core/internal:__pkg__",
|
||||
"//location/nearby/setup/core/internal:__pkg__",
|
||||
"//core:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//platform:types",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/port:string",
|
||||
"//platform/base",
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//proto:connections_enums_portable_proto",
|
||||
"//absl/strings",
|
||||
"//absl/types:variant",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "check_compilation",
|
||||
srcs = ["check_compilation.cc"],
|
||||
cc_test(
|
||||
name = "core_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"core_test.cc",
|
||||
"listeners_test.cc",
|
||||
"payload_test.cc",
|
||||
"status_test.cc",
|
||||
"strategy_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":core",
|
||||
":types",
|
||||
"//platform:types",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//platform/impl/shared:file",
|
||||
"//platform/impl/shared/sample:sample_wifi_medium",
|
||||
"//platform/port:string",
|
||||
":core_types",
|
||||
"//core/internal",
|
||||
"//core/internal:internal_test",
|
||||
"//platform/base",
|
||||
"//platform/impl/g3", # build_cleaner: keep
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//testing/base/public:gunit_main",
|
||||
"//absl/strings",
|
||||
"//absl/time",
|
||||
"//absl/types:variant",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
add_library(core STATIC)
|
||||
|
||||
target_sources(core
|
||||
PUBLIC
|
||||
core.h
|
||||
)
|
||||
|
||||
target_include_directories(core
|
||||
PUBLIC
|
||||
${PROJECT_SOURCE_DIR}/cpp
|
||||
)
|
||||
|
||||
target_link_libraries(core
|
||||
PUBLIC
|
||||
core_internal
|
||||
platform_types
|
||||
)
|
||||
|
||||
add_library(core_types STATIC)
|
||||
|
||||
target_sources(core_types
|
||||
PRIVATE
|
||||
payload.cc
|
||||
strategy.cc
|
||||
PUBLIC
|
||||
listeners.h
|
||||
options.h
|
||||
params.h
|
||||
payload.h
|
||||
status.h
|
||||
strategy.h
|
||||
)
|
||||
|
||||
target_link_libraries(core_types
|
||||
PUBLIC
|
||||
platform_api
|
||||
platform_port_string
|
||||
platform_types
|
||||
platform_utils
|
||||
)
|
||||
|
||||
add_executable(core_build_test
|
||||
check_compilation.cc
|
||||
)
|
||||
|
||||
target_link_libraries(core_build_test
|
||||
PUBLIC
|
||||
absl::strings
|
||||
core
|
||||
core_types
|
||||
platform_impl_g3
|
||||
platform_impl_shared_file
|
||||
platform_impl_shared_posix_lock
|
||||
platform_impl_shared_sample
|
||||
platform_port_string
|
||||
platform_types
|
||||
platform_utils
|
||||
)
|
||||
|
||||
add_subdirectory(internal)
|
||||
@@ -1,126 +0,0 @@
|
||||
#include <vector>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/params.h"
|
||||
#include "core/payload.h"
|
||||
#include "core/status.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/byte_array.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"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
using TestPlatform = platform::ImplementationPlatform;
|
||||
|
||||
class ResultListenerImpl : public ResultListener {
|
||||
public:
|
||||
void onResult(Status::Value status) override {}
|
||||
};
|
||||
|
||||
class ConnectionLifecycleListenerImpl : public ConnectionLifecycleListener {
|
||||
public:
|
||||
void onConnectionInitiated(ConstPtr<OnConnectionInitiatedParams>
|
||||
on_connection_initiated_params) override {}
|
||||
void onConnectionResult(
|
||||
ConstPtr<OnConnectionResultParams> on_connection_result_params) override {
|
||||
}
|
||||
void onDisconnected(
|
||||
ConstPtr<OnDisconnectedParams> on_disconnected_params) override {}
|
||||
void onBandwidthChanged(
|
||||
ConstPtr<OnBandwidthChangedParams> on_bandwidth_changed_params) override {
|
||||
}
|
||||
};
|
||||
|
||||
class DiscoveryListenerImpl : public DiscoveryListener {
|
||||
public:
|
||||
void onEndpointFound(
|
||||
ConstPtr<OnEndpointFoundParams> on_endpoint_found_params) override {}
|
||||
void onEndpointLost(
|
||||
ConstPtr<OnEndpointLostParams> on_endpoint_lost_params) override {}
|
||||
};
|
||||
|
||||
class PayloadListenerImpl : public PayloadListener {
|
||||
public:
|
||||
void onPayloadReceived(
|
||||
ConstPtr<OnPayloadReceivedParams> on_payload_received_params) override {}
|
||||
void onPayloadTransferUpdate(ConstPtr<OnPayloadTransferUpdateParams>
|
||||
on_payload_transfer_update_params) override {
|
||||
}
|
||||
};
|
||||
|
||||
void check_compilation() {
|
||||
Core<TestPlatform> core;
|
||||
|
||||
const string name = "name";
|
||||
const string service_id = "service_id";
|
||||
const string remote_endpoint_id = "remote_endpoint_id";
|
||||
|
||||
core.startAdvertising(MakeConstPtr(new StartAdvertisingParams(
|
||||
MakePtr(new ResultListenerImpl()), name, service_id,
|
||||
AdvertisingOptions(Strategy::kP2PCluster,
|
||||
/* auto_upgrade_bandwidth= */ false,
|
||||
/* enforce_topology_constraints= */ false),
|
||||
MakePtr(new ConnectionLifecycleListenerImpl()))));
|
||||
|
||||
core.stopAdvertising(MakeConstPtr(new StopAdvertisingParams()));
|
||||
|
||||
core.startDiscovery(MakeConstPtr(
|
||||
new StartDiscoveryParams(MakePtr(new ResultListenerImpl()), service_id,
|
||||
DiscoveryOptions(Strategy::kP2PCluster),
|
||||
MakePtr(new DiscoveryListenerImpl()))));
|
||||
|
||||
core.stopDiscovery(MakeConstPtr(new StopDiscoveryParams()));
|
||||
|
||||
core.requestConnection(MakeConstPtr(new RequestConnectionParams(
|
||||
MakePtr(new ResultListenerImpl()), name, remote_endpoint_id,
|
||||
MakePtr(new ConnectionLifecycleListenerImpl()))));
|
||||
|
||||
core.acceptConnection(MakeConstPtr(new AcceptConnectionParams(
|
||||
MakePtr(new ResultListenerImpl()), remote_endpoint_id,
|
||||
MakePtr(new PayloadListenerImpl()))));
|
||||
|
||||
core.rejectConnection(MakeConstPtr(new RejectConnectionParams(
|
||||
MakePtr(new ResultListenerImpl()), remote_endpoint_id)));
|
||||
|
||||
core.initiateBandwidthUpgrade(MakeConstPtr(new InitiateBandwidthUpgradeParams(
|
||||
MakePtr(new ResultListenerImpl()), remote_endpoint_id)));
|
||||
|
||||
core.sendPayload(MakeConstPtr(new SendPayloadParams(
|
||||
MakePtr(new ResultListenerImpl()),
|
||||
std::vector<string>(1, remote_endpoint_id),
|
||||
ConstifyPtr(
|
||||
Payload::fromBytes(MakeConstPtr(new ByteArray("bytes", 5)))))));
|
||||
|
||||
core.cancelPayload(MakeConstPtr(
|
||||
new CancelPayloadParams(MakePtr(new ResultListenerImpl()), 1)));
|
||||
|
||||
core.sendPayload(MakeConstPtr(new SendPayloadParams(
|
||||
MakePtr(new ResultListenerImpl()),
|
||||
std::vector<string>(2, remote_endpoint_id),
|
||||
ConstifyPtr(Payload::fromFile(MakePtr(
|
||||
new InputFileImpl("/some/arbitrary/file/path.txt", 1024)))))));
|
||||
|
||||
core.cancelPayload(MakeConstPtr(
|
||||
new CancelPayloadParams(MakePtr(new ResultListenerImpl()), 2)));
|
||||
|
||||
core.disconnectFromEndpoint(
|
||||
MakeConstPtr(new DisconnectFromEndpointParams(remote_endpoint_id)));
|
||||
|
||||
core.stopAllEndpoints(MakeConstPtr(
|
||||
new StopAllEndpointsParams(MakePtr(new ResultListenerImpl()))));
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
int main() {
|
||||
location::nearby::connections::check_compilation();
|
||||
return 0;
|
||||
}
|
||||
+72
-93
@@ -1,135 +1,114 @@
|
||||
#include "core/core.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "core/options.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
Core<Platform>::Core()
|
||||
: client_proxy_(new ClientProxy<Platform>()),
|
||||
service_controller_router_(new ServiceControllerRouter<Platform>()) {}
|
||||
constexpr absl::Duration Core::kWaitForDisconnect;
|
||||
|
||||
template <typename Platform>
|
||||
Core<Platform>::~Core() {
|
||||
service_controller_router_->clientDisconnecting(client_proxy_.get());
|
||||
Core::~Core() {
|
||||
CountDownLatch latch(1);
|
||||
router_.ClientDisconnecting(
|
||||
&client_, {
|
||||
.result_cb = [&latch](Status) { latch.CountDown(); },
|
||||
});
|
||||
if (!latch.Await(kWaitForDisconnect).result()) {
|
||||
NEARBY_LOG(FATAL, "Unable to shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::startAdvertising(
|
||||
ConstPtr<StartAdvertisingParams> start_advertising_params) {
|
||||
assert(!start_advertising_params->result_listener.isNull());
|
||||
assert(!start_advertising_params->connection_lifecycle_listener.isNull());
|
||||
assert(!start_advertising_params->service_id.empty());
|
||||
assert(start_advertising_params->advertising_options.strategy.isValid());
|
||||
void Core::StartAdvertising(absl::string_view service_id,
|
||||
ConnectionOptions options,
|
||||
ConnectionRequestInfo info,
|
||||
ResultCallback callback) {
|
||||
assert(!service_id.empty());
|
||||
assert(options.strategy.IsValid());
|
||||
|
||||
service_controller_router_->startAdvertising(client_proxy_.get(),
|
||||
start_advertising_params);
|
||||
router_.StartAdvertising(&client_, service_id, options, info, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::stopAdvertising(
|
||||
ConstPtr<StopAdvertisingParams> stop_advertising_params) {
|
||||
service_controller_router_->stopAdvertising(client_proxy_.get(),
|
||||
stop_advertising_params);
|
||||
void Core::StopAdvertising(const ResultCallback callback) {
|
||||
router_.StopAdvertising(&client_, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::startDiscovery(
|
||||
ConstPtr<StartDiscoveryParams> start_discovery_params) {
|
||||
assert(!start_discovery_params->result_listener.isNull());
|
||||
assert(!start_discovery_params->discovery_listener.isNull());
|
||||
assert(!start_discovery_params->service_id.empty());
|
||||
assert(start_discovery_params->discovery_options.strategy.isValid());
|
||||
void Core::StartDiscovery(absl::string_view service_id,
|
||||
ConnectionOptions options, DiscoveryListener listener,
|
||||
ResultCallback callback) {
|
||||
assert(!service_id.empty());
|
||||
assert(options.strategy.IsValid());
|
||||
|
||||
service_controller_router_->startDiscovery(client_proxy_.get(),
|
||||
start_discovery_params);
|
||||
router_.StartDiscovery(&client_, service_id, options, listener, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::stopDiscovery(
|
||||
ConstPtr<StopDiscoveryParams> stop_discovery_params) {
|
||||
service_controller_router_->stopDiscovery(client_proxy_.get(),
|
||||
stop_discovery_params);
|
||||
void Core::InjectEndpoint(absl::string_view service_id,
|
||||
OutOfBandConnectionMetadata metadata,
|
||||
ResultCallback callback) {
|
||||
router_.InjectEndpoint(&client_, service_id, metadata, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::requestConnection(
|
||||
ConstPtr<RequestConnectionParams> request_connection_params) {
|
||||
assert(!request_connection_params->result_listener.isNull());
|
||||
assert(!request_connection_params->connection_lifecycle_listener.isNull());
|
||||
assert(!request_connection_params->remote_endpoint_id.empty());
|
||||
|
||||
service_controller_router_->requestConnection(client_proxy_.get(),
|
||||
request_connection_params);
|
||||
void Core::StopDiscovery(ResultCallback callback) {
|
||||
router_.StopDiscovery(&client_, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::acceptConnection(
|
||||
ConstPtr<AcceptConnectionParams> accept_connection_params) {
|
||||
assert(!accept_connection_params->result_listener.isNull());
|
||||
assert(!accept_connection_params->payload_listener.isNull());
|
||||
assert(!accept_connection_params->remote_endpoint_id.empty());
|
||||
void Core::RequestConnection(absl::string_view endpoint_id,
|
||||
ConnectionRequestInfo info,
|
||||
ConnectionOptions options,
|
||||
ResultCallback callback) {
|
||||
assert(!endpoint_id.empty());
|
||||
|
||||
service_controller_router_->acceptConnection(client_proxy_.get(),
|
||||
accept_connection_params);
|
||||
router_.RequestConnection(&client_, endpoint_id, info, options, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::rejectConnection(
|
||||
ConstPtr<RejectConnectionParams> reject_connection_params) {
|
||||
assert(!reject_connection_params->result_listener.isNull());
|
||||
assert(!reject_connection_params->remote_endpoint_id.empty());
|
||||
void Core::AcceptConnection(absl::string_view endpoint_id,
|
||||
PayloadListener listener, ResultCallback callback) {
|
||||
assert(!endpoint_id.empty());
|
||||
|
||||
service_controller_router_->rejectConnection(client_proxy_.get(),
|
||||
reject_connection_params);
|
||||
router_.AcceptConnection(&client_, endpoint_id, listener, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::initiateBandwidthUpgrade(
|
||||
ConstPtr<InitiateBandwidthUpgradeParams>
|
||||
initiate_bandwidth_upgrade_params) {
|
||||
service_controller_router_->initiateBandwidthUpgrade(
|
||||
client_proxy_.get(), initiate_bandwidth_upgrade_params);
|
||||
void Core::RejectConnection(absl::string_view endpoint_id,
|
||||
ResultCallback callback) {
|
||||
assert(!endpoint_id.empty());
|
||||
|
||||
router_.RejectConnection(&client_, endpoint_id, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::sendPayload(
|
||||
ConstPtr<SendPayloadParams> send_payload_params) {
|
||||
assert(!send_payload_params->result_listener.isNull());
|
||||
assert(!send_payload_params->remote_endpoint_ids.empty());
|
||||
assert(!send_payload_params->payload.isNull());
|
||||
// TODO(tracyzhou): Do sanity check on payload based on payload type.
|
||||
|
||||
service_controller_router_->sendPayload(client_proxy_.get(),
|
||||
send_payload_params);
|
||||
void Core::InitiateBandwidthUpgrade(absl::string_view endpoint_id,
|
||||
ResultCallback callback) {
|
||||
router_.InitiateBandwidthUpgrade(&client_, endpoint_id, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::cancelPayload(
|
||||
ConstPtr<CancelPayloadParams> cancel_payload_params) {
|
||||
assert(!cancel_payload_params->result_listener.isNull());
|
||||
assert(cancel_payload_params->payload_id != 0);
|
||||
void Core::SendPayload(absl::Span<const std::string> endpoint_ids,
|
||||
Payload payload, ResultCallback callback) {
|
||||
assert(payload.GetType() != Payload::Type::kUnknown);
|
||||
assert(!endpoint_ids.empty());
|
||||
|
||||
service_controller_router_->cancelPayload(client_proxy_.get(),
|
||||
cancel_payload_params);
|
||||
router_.SendPayload(&client_, endpoint_ids, std::move(payload), callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::disconnectFromEndpoint(
|
||||
ConstPtr<DisconnectFromEndpointParams> disconnect_from_endpoint_params) {
|
||||
assert(!disconnect_from_endpoint_params->remote_endpoint_id.empty());
|
||||
void Core::CancelPayload(std::int64_t payload_id, ResultCallback callback) {
|
||||
assert(payload_id != 0);
|
||||
|
||||
service_controller_router_->disconnectFromEndpoint(
|
||||
client_proxy_.get(), disconnect_from_endpoint_params);
|
||||
router_.CancelPayload(&client_, payload_id, callback);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void Core<Platform>::stopAllEndpoints(
|
||||
ConstPtr<StopAllEndpointsParams> stop_all_endpoints_params) {
|
||||
service_controller_router_->stopAllEndpoints(client_proxy_.get(),
|
||||
stop_all_endpoints_params);
|
||||
void Core::DisconnectFromEndpoint(absl::string_view endpoint_id,
|
||||
ResultCallback callback) {
|
||||
assert(!endpoint_id.empty());
|
||||
|
||||
router_.DisconnectFromEndpoint(&client_, endpoint_id, callback);
|
||||
}
|
||||
|
||||
void Core::StopAllEndpoints(ResultCallback callback) {
|
||||
router_.StopAllEndpoints(&client_, callback);
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
+205
-64
@@ -1,88 +1,229 @@
|
||||
#ifndef CORE_CORE_H_
|
||||
#define CORE_CORE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/offline_service_controller.h"
|
||||
#include "core/internal/service_controller.h"
|
||||
#include "core/internal/service_controller_router.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#include "core/params.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/*
|
||||
* This class defines the API of the Nearby Connections Core library.
|
||||
*
|
||||
* Each passed-in Platform must provide a set of primitives with platform-
|
||||
* specific implementations. The Platform class must provide factory functions
|
||||
* for the following primitives:
|
||||
*
|
||||
* SingleThreadExecutor
|
||||
* MultiThreadExecutor
|
||||
* ScheduledExecutor
|
||||
* Lock
|
||||
* CountDownLatch
|
||||
* AtomicBoolean
|
||||
* AtomicReference
|
||||
* SettableFuture
|
||||
* BluetoothAdapter
|
||||
* BluetoothClassicMedium
|
||||
* HashUtils
|
||||
* ThreadUtils
|
||||
* SystemClock
|
||||
* ConditionVariable
|
||||
*
|
||||
* A sample Platform definitions can be found at
|
||||
* //platform/impl/shared/sample/sample_platform.cc
|
||||
*
|
||||
* It is no longer necessary to parametrize system types with a platform type.
|
||||
* New, recommended approach is to define platform support by implementing
|
||||
* static methods of "location::nearby::platform::ImplementationPlatform" class.
|
||||
* every library class that needs platform support, must include platform
|
||||
* header "platform/api/platform.h" and use it.
|
||||
* To keep textual compatibility, one could define the following alias
|
||||
* "using Platform = platform::ImplementationPlatform;".
|
||||
* this will replace the "template <typename Platform>" declaration.
|
||||
*
|
||||
* As an added benefit, this will allow to not include *.cc files from *.h,
|
||||
* and let more static analysis happen at compiler stage.
|
||||
*/
|
||||
template <typename Platform>
|
||||
// This class defines the API of the Nearby Connections Core library.
|
||||
class Core {
|
||||
public:
|
||||
Core();
|
||||
explicit Core(std::function<ServiceController*()> factory =
|
||||
[]() { return new OfflineServiceController; })
|
||||
: router_(factory) {}
|
||||
~Core();
|
||||
Core(Core&&) = default;
|
||||
Core& operator=(Core&&) = default;
|
||||
|
||||
void startAdvertising(
|
||||
ConstPtr<StartAdvertisingParams> start_advertising_params);
|
||||
void stopAdvertising(ConstPtr<StopAdvertisingParams> stop_advertising_params);
|
||||
void startDiscovery(ConstPtr<StartDiscoveryParams> start_discovery_params);
|
||||
void stopDiscovery(ConstPtr<StopDiscoveryParams> stop_discovery_params);
|
||||
void requestConnection(
|
||||
ConstPtr<RequestConnectionParams> request_connection_params);
|
||||
void acceptConnection(
|
||||
ConstPtr<AcceptConnectionParams> accept_connection_params);
|
||||
void rejectConnection(
|
||||
ConstPtr<RejectConnectionParams> reject_connection_params);
|
||||
void initiateBandwidthUpgrade(ConstPtr<InitiateBandwidthUpgradeParams>
|
||||
initiate_bandwidth_upgrade_params);
|
||||
void sendPayload(ConstPtr<SendPayloadParams> send_payload_params);
|
||||
void cancelPayload(ConstPtr<CancelPayloadParams> cancel_payload_params);
|
||||
void disconnectFromEndpoint(
|
||||
ConstPtr<DisconnectFromEndpointParams> disconnect_from_endpoint_params);
|
||||
void stopAllEndpoints(
|
||||
ConstPtr<StopAllEndpointsParams> stop_all_endpoints_params);
|
||||
// Starts advertising an endpoint for a local app.
|
||||
//
|
||||
// service_id - An identifier to advertise your app to other endpoints.
|
||||
// This can be an arbitrary string, so long as it uniquely
|
||||
// identifies your service. A good default is to use your
|
||||
// app's package name.
|
||||
// options - The options for advertising.
|
||||
// info - Connection parameters:
|
||||
// > name - A human readable name for this endpoint, to appear on
|
||||
// other devices.
|
||||
// > listener - A callback notified when remote endpoints request a
|
||||
// connection to this endpoint.
|
||||
// callback - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if advertising started successfully.
|
||||
// Status::STATUS_ALREADY_ADVERTISING if the app is already advertising.
|
||||
// Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently
|
||||
// connected to remote endpoints; call StopAllEndpoints first.
|
||||
void StartAdvertising(absl::string_view service_id, ConnectionOptions options,
|
||||
ConnectionRequestInfo info, ResultCallback callback);
|
||||
|
||||
// Stops advertising a local endpoint. Should be called after calling
|
||||
// StartAdvertising, as soon as the application no longer needs to advertise
|
||||
// itself or goes inactive. Payloads can still be sent to connected
|
||||
// endpoints after advertising ends.
|
||||
//
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if none of the above errors occurred.
|
||||
void StopAdvertising(ResultCallback callback);
|
||||
|
||||
// Starts discovery for remote endpoints with the specified service ID.
|
||||
//
|
||||
// service_id - The ID for the service to be discovered, as specified in
|
||||
// the corresponding call to StartAdvertising.
|
||||
// listener - A callback notified when a remote endpoint is discovered.
|
||||
// options - The options for discovery.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if discovery started successfully.
|
||||
// Status::STATUS_ALREADY_DISCOVERING if the app is already
|
||||
// discovering the specified service.
|
||||
// Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently
|
||||
// connected to remote endpoints; call StopAllEndpoints first.
|
||||
void StartDiscovery(absl::string_view service_id, ConnectionOptions options,
|
||||
DiscoveryListener listener, ResultCallback callback);
|
||||
|
||||
// Stops discovery for remote endpoints, after a previous call to
|
||||
// StartDiscovery, when the client no longer needs to discover endpoints or
|
||||
// goes inactive. Payloads can still be sent to connected endpoints after
|
||||
// discovery ends.
|
||||
//
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if none of the above errors occurred.
|
||||
void StopDiscovery(ResultCallback callback);
|
||||
|
||||
// Invokes the discovery callback from a previous call to StartDiscovery()
|
||||
// with the given endpoint info. The previous call to StartDiscovery() must
|
||||
// have been passed ConnectionOptions with is_out_of_band_connection == true.
|
||||
//
|
||||
// service_id - The ID for the service to be discovered, as
|
||||
// specified in the corresponding call to
|
||||
// StartDiscovery().
|
||||
// metadata - Metadata used in order to inject the endpoint.
|
||||
// result_cb - to access the status of the operation when
|
||||
// available.
|
||||
// Possible status codes include:
|
||||
// Status::kSuccess if endpoint injection was attempted.
|
||||
// Status::kError if bluetooth_mac_address is malformed.
|
||||
// Status::kOutOfOrderApiCall if the app is not discovering.
|
||||
void InjectEndpoint(absl::string_view service_id,
|
||||
OutOfBandConnectionMetadata metadata,
|
||||
ResultCallback callback);
|
||||
|
||||
// Sends a request to connect to a remote endpoint.
|
||||
//
|
||||
// endpoint_id - The identifier for the remote endpoint to which a
|
||||
// connection request will be sent. Should match the value
|
||||
// provided in a call to
|
||||
// DiscoveryListener::endpoint_found_cb()
|
||||
// info - Connection parameters:
|
||||
// > name - A human readable name for the local endpoint, to appear on
|
||||
// the remote endpoint.
|
||||
// > listener - A callback notified when the remote endpoint sends a
|
||||
// response to the connection request.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if the connection request was sent.
|
||||
// Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT if the app already
|
||||
// has a connection to the specified endpoint.
|
||||
// Status::STATUS_RADIO_ERROR if we failed to connect because of an
|
||||
// issue with Bluetooth/WiFi.
|
||||
// Status::STATUS_ERROR if we failed to connect for any other reason.
|
||||
void RequestConnection(absl::string_view endpoint_id,
|
||||
ConnectionRequestInfo info, ConnectionOptions options,
|
||||
ResultCallback callback);
|
||||
|
||||
// Accepts a connection to a remote endpoint. This method must be called
|
||||
// before Payloads can be exchanged with the remote endpoint.
|
||||
//
|
||||
// endpoint_id - The identifier for the remote endpoint. Should match the
|
||||
// value provided in a call to
|
||||
// ConnectionListener::onConnectionInitiated.
|
||||
// listener - A callback for payloads exchanged with the remote endpoint.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if the connection request was accepted.
|
||||
// Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT if the app already.
|
||||
// has a connection to the specified endpoint.
|
||||
void AcceptConnection(absl::string_view endpoint_id, PayloadListener listener,
|
||||
ResultCallback callback);
|
||||
|
||||
// Rejects a connection to a remote endpoint.
|
||||
//
|
||||
// endpoint_id - The identifier for the remote endpoint. Should match the
|
||||
// value provided in a call to
|
||||
// ConnectionListener::onConnectionInitiated().
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK} if the connection request was rejected.
|
||||
// Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT} if the app already
|
||||
// has a connection to the specified endpoint.
|
||||
void RejectConnection(absl::string_view endpoint_id, ResultCallback callback);
|
||||
|
||||
// Sends a Payload to a remote endpoint. Payloads can only be sent to remote
|
||||
// endpoints once a notice of connection acceptance has been delivered via
|
||||
// ConnectionListener::onConnectionResult().
|
||||
//
|
||||
// endpoint_ids - Array of remote endpoint identifiers for the to which the
|
||||
// payload should be sent.
|
||||
// payload - The Payload to be sent.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OUT_OF_ORDER_API_CALL if the device has not first
|
||||
// performed advertisement or discovery (to set the Strategy.
|
||||
// Status::STATUS_ENDPOINT_UNKNOWN if there's no active (or pending)
|
||||
// connection to the remote endpoint.
|
||||
// Status::STATUS_OK if none of the above errors occurred. Note that this
|
||||
// indicates that Nearby Connections will attempt to send the Payload,
|
||||
// but not that the send has successfully completed yet. Errors might
|
||||
// still occur during transmission (and at different times for
|
||||
// different endpoints), and will be delivered via
|
||||
// PayloadCallback#onPayloadTransferUpdate.
|
||||
void SendPayload(absl::Span<const std::string> endpoint_ids, Payload payload,
|
||||
ResultCallback callback);
|
||||
|
||||
// Cancels a Payload currently in-flight to or from remote endpoint(s).
|
||||
//
|
||||
// payload_id - The identifier for the Payload to be canceled.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK if none of the above errors occurred.
|
||||
void CancelPayload(std::int64_t payload_id, ResultCallback callback);
|
||||
|
||||
// Disconnects from a remote endpoint. {@link Payload}s can no longer be sent
|
||||
// to or received from the endpoint after this method is called.
|
||||
//
|
||||
// endpoint_id - The identifier for the remote endpoint to disconnect from.
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK - finished successfully.
|
||||
void DisconnectFromEndpoint(absl::string_view endpoint_id,
|
||||
ResultCallback callback);
|
||||
|
||||
// Disconnects from, and removes all traces of, all connected and/or
|
||||
// discovered endpoints. This call is expected to be preceded by a call to
|
||||
// StopAdvertising or StartDiscovery as needed. After calling
|
||||
// StopAllEndpoints, no further operations with remote endpoints will be
|
||||
// possible until a new call to one of StartAdvertising() or StartDiscovery().
|
||||
//
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK - finished successfully.
|
||||
void StopAllEndpoints(ResultCallback callback);
|
||||
|
||||
// Sends a request to initiate connection bandwidth upgrade.
|
||||
//
|
||||
// endpoint_id - The identifier for the remote endpoint which will be
|
||||
// switching to a higher connection data rate and possibly
|
||||
// different wireless protocol. On success, calls
|
||||
// ConnectionListener::bandwidth_changed_cb().
|
||||
// result_cb - to access the status of the operation when available.
|
||||
// Possible status codes include:
|
||||
// Status::STATUS_OK - finished successfully.
|
||||
void InitiateBandwidthUpgrade(absl::string_view endpoint_id,
|
||||
ResultCallback callback);
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<ClientProxy<Platform> > > client_proxy_;
|
||||
ScopedPtr<Ptr<ServiceControllerRouter<Platform> > >
|
||||
service_controller_router_;
|
||||
static constexpr absl::Duration kWaitForDisconnect = absl::Milliseconds(5000);
|
||||
|
||||
ClientProxy client_;
|
||||
ServiceControllerRouter router_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/core.cc"
|
||||
|
||||
#endif // CORE_CORE_H_
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "core_v2/core.h"
|
||||
#include "core/core.h"
|
||||
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/mock_service_controller.h"
|
||||
#include "core_v2/internal/service_controller.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/mock_service_controller.h"
|
||||
#include "core/internal/service_controller.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
+103
-91
@@ -1,89 +1,94 @@
|
||||
cc_library(
|
||||
name = "internal",
|
||||
srcs = [
|
||||
"bandwidth_upgrade_manager.cc",
|
||||
"base_bandwidth_upgrade_handler.cc",
|
||||
"base_endpoint_channel.cc",
|
||||
"base_pcp_handler.cc",
|
||||
"ble_advertisement.cc",
|
||||
"ble_endpoint_channel.cc",
|
||||
"bluetooth_device_name.cc",
|
||||
"bluetooth_endpoint_channel.cc",
|
||||
"bwu_manager.cc",
|
||||
"client_proxy.cc",
|
||||
"encryption_runner.cc",
|
||||
"endpoint_channel_manager.cc",
|
||||
"endpoint_manager.cc",
|
||||
"internal_payload.cc",
|
||||
"internal_payload.h",
|
||||
"loop_runner.cc",
|
||||
"loop_runner.h",
|
||||
"internal_payload_factory.cc",
|
||||
"offline_frames.cc",
|
||||
"offline_service_controller.cc",
|
||||
"p2p_cluster_pcp_handler.cc",
|
||||
"p2p_point_to_point_pcp_handler.cc",
|
||||
"p2p_star_pcp_handler.cc",
|
||||
"payload_manager.cc",
|
||||
"pcp_manager.cc",
|
||||
"service_controller_router.cc",
|
||||
"webrtc_bwu_handler.cc",
|
||||
"webrtc_endpoint_channel.cc",
|
||||
"wifi_lan_endpoint_channel.cc",
|
||||
"wifi_lan_service_info.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"bandwidth_upgrade_handler.h",
|
||||
"bandwidth_upgrade_manager.h",
|
||||
"base_bandwidth_upgrade_handler.h",
|
||||
"base_bwu_handler.h",
|
||||
"base_endpoint_channel.h",
|
||||
"base_pcp_handler.cc",
|
||||
"base_pcp_handler.h",
|
||||
"ble_advertisement.h",
|
||||
"ble_compat.h",
|
||||
"ble_endpoint_channel.h",
|
||||
"bluetooth_device_name.h",
|
||||
"bluetooth_endpoint_channel.h",
|
||||
"client_proxy.cc",
|
||||
"bwu_handler.h",
|
||||
"bwu_manager.h",
|
||||
"client_proxy.h",
|
||||
"encryption_runner.cc",
|
||||
"encryption_runner.h",
|
||||
"endpoint_channel.h",
|
||||
"endpoint_channel_manager.h",
|
||||
"endpoint_manager.cc",
|
||||
"endpoint_manager.h",
|
||||
"internal_payload_factory.cc",
|
||||
"internal_payload.h",
|
||||
"internal_payload_factory.h",
|
||||
"medium_manager.cc",
|
||||
"medium_manager.h",
|
||||
"offline_frames.h",
|
||||
"offline_service_controller.cc",
|
||||
"offline_service_controller.h",
|
||||
"p2p_cluster_pcp_handler.cc",
|
||||
"p2p_cluster_pcp_handler.h",
|
||||
"p2p_point_to_point_pcp_handler.cc",
|
||||
"p2p_point_to_point_pcp_handler.h",
|
||||
"p2p_star_pcp_handler.cc",
|
||||
"p2p_star_pcp_handler.h",
|
||||
"payload_manager.cc",
|
||||
"payload_manager.h",
|
||||
"pcp.h",
|
||||
"pcp_handler.h",
|
||||
"pcp_manager.cc",
|
||||
"pcp_manager.h",
|
||||
"service_controller.h",
|
||||
"service_controller_router.cc",
|
||||
"service_controller_router.h",
|
||||
"webrtc_bwu_handler.h",
|
||||
"webrtc_endpoint_channel.h",
|
||||
"wifi_lan_endpoint_channel.h",
|
||||
"wifi_lan_service_info.h",
|
||||
"wifi_lan_upgrade_handler.cc",
|
||||
"wifi_lan_upgrade_handler.h",
|
||||
],
|
||||
visibility = [
|
||||
"//core:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//core:types",
|
||||
"//core:core_types",
|
||||
"//core/internal:message_lite",
|
||||
"//core/internal/mediums",
|
||||
"//core/internal/mediums:utils",
|
||||
"//core/internal/mediums/webrtc",
|
||||
"//proto/connections:offline_wire_formats_portable_proto",
|
||||
"//platform:logging",
|
||||
"//platform:types",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/port:string",
|
||||
"//platform/base",
|
||||
"//platform/base:util",
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//proto:connections_enums_portable_proto",
|
||||
"//net/proto2/compat/public:proto2_lite",
|
||||
"//securegcm:ukey2",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/container:btree",
|
||||
"//absl/container:flat_hash_map",
|
||||
"//absl/container:flat_hash_set",
|
||||
"//absl/functional:bind_front",
|
||||
"//absl/memory",
|
||||
"//absl/strings",
|
||||
"//absl/time",
|
||||
"//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
# TODO(apolyudov): remove when api v2 rework is done.
|
||||
cc_library(
|
||||
name = "message_lite",
|
||||
hdrs = [
|
||||
@@ -91,75 +96,82 @@ cc_library(
|
||||
],
|
||||
visibility = [
|
||||
"//core:__subpackages__",
|
||||
"//core_v2:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//net/proto2/compat/public:proto2_lite",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "base_endpoint_channel_test",
|
||||
srcs = ["base_endpoint_channel_test.cc"],
|
||||
deps = [
|
||||
":internal",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//proto:connections_enums_portable_proto",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "bluetooth_device_name_test",
|
||||
srcs = ["bluetooth_device_name_test.cc"],
|
||||
deps = [
|
||||
":internal",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//platform/port:string",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ble_advertisement_test",
|
||||
srcs = ["ble_advertisement_test.cc"],
|
||||
deps = [
|
||||
":internal",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//platform/port:string",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wifi_lan_service_info_test",
|
||||
srcs = ["wifi_lan_service_info_test.cc"],
|
||||
deps = [
|
||||
":internal",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//platform/port:string",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "offline_frames_test",
|
||||
cc_library(
|
||||
name = "internal_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"offline_frames_test.cc",
|
||||
"offline_simulation_user.cc",
|
||||
"simulation_user.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"mock_service_controller.h",
|
||||
"offline_simulation_user.h",
|
||||
"simulation_user.h",
|
||||
],
|
||||
visibility = [
|
||||
"//core:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":internal",
|
||||
"//core:core_types",
|
||||
"//platform/base",
|
||||
"//platform/base:test_util",
|
||||
"//platform/public:types",
|
||||
"//testing/base/public:gunit",
|
||||
"//absl/functional:bind_front",
|
||||
"//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "core_internal_test",
|
||||
size = "small",
|
||||
timeout = "moderate",
|
||||
srcs = [
|
||||
"base_endpoint_channel_test.cc",
|
||||
"base_pcp_handler_test.cc",
|
||||
"ble_advertisement_test.cc",
|
||||
"bluetooth_device_name_test.cc",
|
||||
"bwu_manager_test.cc",
|
||||
"client_proxy_test.cc",
|
||||
"encryption_runner_test.cc",
|
||||
"endpoint_channel_manager_test.cc",
|
||||
"endpoint_manager_test.cc",
|
||||
"internal_payload_factory_test.cc",
|
||||
"offline_frames_test.cc",
|
||||
"offline_service_controller_test.cc",
|
||||
"p2p_cluster_pcp_handler_test.cc",
|
||||
"payload_manager_test.cc",
|
||||
"pcp_manager_test.cc",
|
||||
"service_controller_router_test.cc",
|
||||
"wifi_lan_service_info_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":internal",
|
||||
":internal_test",
|
||||
"//core:core_types",
|
||||
"//core/internal/mediums",
|
||||
"//proto/connections:offline_wire_formats_portable_proto",
|
||||
"//platform:types",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//platform/base",
|
||||
"//platform/base:test_util",
|
||||
"//platform/impl/g3", # build_cleaner: keep
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//proto:connections_enums_portable_proto",
|
||||
"//securegcm:ukey2",
|
||||
"//testing/base/public:gunit",
|
||||
"//testing/base/public:gunit_main",
|
||||
"//absl/container:flat_hash_set",
|
||||
"//absl/strings",
|
||||
"//absl/synchronization",
|
||||
"//absl/time",
|
||||
"//absl/types:span",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
add_library(core_internal STATIC)
|
||||
|
||||
target_sources(core_internal
|
||||
PRIVATE
|
||||
bandwidth_upgrade_manager.cc
|
||||
base_bandwidth_upgrade_handler.cc
|
||||
base_endpoint_channel.cc
|
||||
ble_advertisement.cc
|
||||
ble_endpoint_channel.cc
|
||||
bluetooth_device_name.cc
|
||||
bluetooth_endpoint_channel.cc
|
||||
endpoint_channel_manager.cc
|
||||
internal_payload.cc
|
||||
internal_payload.h
|
||||
loop_runner.cc
|
||||
loop_runner.h
|
||||
offline_frames.cc
|
||||
wifi_lan_service_info.cc
|
||||
wifi_lan_endpoint_channel.cc
|
||||
PUBLIC
|
||||
bandwidth_upgrade_handler.h
|
||||
bandwidth_upgrade_manager.h
|
||||
base_bandwidth_upgrade_handler.h
|
||||
base_endpoint_channel.h
|
||||
base_pcp_handler.h
|
||||
ble_advertisement.h
|
||||
ble_compat.h
|
||||
ble_endpoint_channel.h
|
||||
bluetooth_device_name.h
|
||||
bluetooth_endpoint_channel.h
|
||||
client_proxy.h
|
||||
encryption_runner.h
|
||||
endpoint_channel.h
|
||||
endpoint_channel_manager.h
|
||||
endpoint_manager.h
|
||||
internal_payload_factory.h
|
||||
medium_manager.h
|
||||
offline_frames.h
|
||||
offline_service_controller.h
|
||||
p2p_cluster_pcp_handler.h
|
||||
p2p_point_to_point_pcp_handler.h
|
||||
p2p_star_pcp_handler.h
|
||||
payload_manager.h
|
||||
pcp.h
|
||||
pcp_handler.h
|
||||
pcp_manager.h
|
||||
service_controller.h
|
||||
service_controller_router.h
|
||||
wifi_lan_endpoint_channel.h
|
||||
wifi_lan_upgrade_handler.h
|
||||
)
|
||||
|
||||
target_link_libraries(core_internal
|
||||
PUBLIC
|
||||
absl::strings
|
||||
core_internal_mediums
|
||||
core_types
|
||||
platform_api
|
||||
platform_port_down_cast
|
||||
platform_port_string
|
||||
platform_types
|
||||
platform_utils
|
||||
proto_connections_enums_cc_proto
|
||||
proto_offline_wire_formats_cc_proto
|
||||
ukey2
|
||||
)
|
||||
|
||||
add_executable(core_internal_test
|
||||
base_endpoint_channel_test.cc
|
||||
bluetooth_device_name_test.cc
|
||||
ble_advertisement_test.cc
|
||||
offline_frames_test.cc
|
||||
wifi_lan_service_info_test.cc
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME core_internal_test
|
||||
COMMAND core_internal_test
|
||||
)
|
||||
|
||||
target_link_libraries(core_internal_test
|
||||
PUBLIC
|
||||
core_internal
|
||||
gmock
|
||||
gtest
|
||||
gtest_main
|
||||
platform_impl_g3
|
||||
platform_impl_shared_posix_condition_variable
|
||||
platform_impl_shared_posix_lock
|
||||
platform_port_string
|
||||
platform_utils
|
||||
)
|
||||
|
||||
add_subdirectory(mediums)
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
#define CORE_INTERNAL_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/api/count_down_latch.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
// Defines the set of methods that need to be implemented to handle the
|
||||
// per-Medium-specific operations needed to upgrade an EndpointChannel.
|
||||
class BandwidthUpgradeHandler {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
|
||||
virtual ~BandwidthUpgradeHandler() {}
|
||||
|
||||
// Reverts any changes made to the device in the process of upgrading
|
||||
// endpoints.
|
||||
virtual void revert() = 0;
|
||||
|
||||
// Cleans up in-progress upgrades after endpoint disconnection.
|
||||
virtual void processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const std::string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) = 0;
|
||||
|
||||
// Initiates the upgrade for the endpoint and starts listening for upgraded
|
||||
// incoming connections on the initiator side of the bandwidth upgrade.
|
||||
virtual void initiateBandwidthUpgradeForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const std::string& endpoint_id) = 0;
|
||||
|
||||
// Processes the BandwidthUpgradeNegotiationFrames that come over the
|
||||
// EndpointChannel on the non-initiator side of the bandwidth upgrade.
|
||||
// TODO(ahlee): Rename parameters in the java code.
|
||||
virtual void processBandwidthUpgradeNegotiationFrame(
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame> bandwidth_upgrade_negotiation,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
const std::string& from_endpoint_id,
|
||||
proto::connections::Medium current_medium) = 0;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
@@ -1,41 +0,0 @@
|
||||
#include "core/internal/bandwidth_upgrade_manager.h"
|
||||
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
BandwidthUpgradeManager::BandwidthUpgradeManager(
|
||||
Ptr<MediumManager<Platform> > medium_manager,
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager,
|
||||
Ptr<EndpointManager<Platform> > endpoint_manager)
|
||||
: endpoint_manager_(endpoint_manager),
|
||||
bandwidth_upgrade_handlers_(),
|
||||
current_bandwidth_upgrade_handler_() {}
|
||||
|
||||
BandwidthUpgradeManager::~BandwidthUpgradeManager() {
|
||||
// TODO(ahlee): Make sure we don't repeat the mistake fixed in cl/201883908.
|
||||
}
|
||||
|
||||
void BandwidthUpgradeManager::initiateBandwidthUpgradeForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
proto::connections::Medium medium) {}
|
||||
|
||||
void BandwidthUpgradeManager::processIncomingOfflineFrame(
|
||||
ConstPtr<OfflineFrame> offline_frame, const string& from_endpoint_id,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
proto::connections::Medium current_medium) {}
|
||||
|
||||
void BandwidthUpgradeManager::processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) {}
|
||||
|
||||
bool BandwidthUpgradeManager::setCurrentBandwidthUpgradeHandler(
|
||||
proto::connections::Medium medium) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_BANDWIDTH_UPGRADE_MANAGER_H_
|
||||
#define CORE_INTERNAL_BANDWIDTH_UPGRADE_MANAGER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "core/internal/bandwidth_upgrade_handler.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/endpoint_manager.h"
|
||||
#include "core/internal/medium_manager.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
// Manages all known {@link BandwidthUpgradeHandler} implementations, delegating
|
||||
// operations to the appropriate one as per the parameters passed in.
|
||||
class BandwidthUpgradeManager
|
||||
: public EndpointManager<
|
||||
platform::ImplementationPlatform>::IncomingOfflineFrameProcessor {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
|
||||
BandwidthUpgradeManager(Ptr<MediumManager<Platform>> medium_manager,
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager,
|
||||
Ptr<EndpointManager<Platform>> endpoint_manager);
|
||||
~BandwidthUpgradeManager() override;
|
||||
|
||||
// This is the point on the initiator side where the
|
||||
// current_bandwidth_upgrade_handler_ is set.
|
||||
void initiateBandwidthUpgradeForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
proto::connections::Medium medium);
|
||||
// This is the point on the non-initiator side where the
|
||||
// current_bandwidth_upgrade_handler_ is set.
|
||||
// @EndpointManagerReaderThread
|
||||
void processIncomingOfflineFrame(
|
||||
ConstPtr<OfflineFrame> offline_frame, const string& from_endpoint_id,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
proto::connections::Medium current_medium) override;
|
||||
// @EndpointManagerReaderThread
|
||||
void processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) override;
|
||||
|
||||
private:
|
||||
bool setCurrentBandwidthUpgradeHandler(proto::connections::Medium medium);
|
||||
|
||||
Ptr<EndpointManager<Platform> > endpoint_manager_;
|
||||
typedef std::map<proto::connections::Medium, Ptr<BandwidthUpgradeHandler>>
|
||||
BandwidthUpgradeHandlersMap;
|
||||
BandwidthUpgradeHandlersMap bandwidth_upgrade_handlers_;
|
||||
Ptr<BandwidthUpgradeHandler> current_bandwidth_upgrade_handler_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_BANDWIDTH_UPGRADE_MANAGER_H_
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "core/internal/base_bandwidth_upgrade_handler.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace {
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
}
|
||||
|
||||
namespace base_bandwidth_upgrade_handler {
|
||||
|
||||
class RevertRunnable : public Runnable {
|
||||
public:
|
||||
void run() override {}
|
||||
};
|
||||
|
||||
class InitiateBandwidthUpgradeForEndpointRunnable : public Runnable {
|
||||
public:
|
||||
void run() override {}
|
||||
};
|
||||
|
||||
class ProcessEndpointDisconnectionRunnable : public Runnable {
|
||||
public:
|
||||
void run() override {}
|
||||
};
|
||||
|
||||
class ProcessBandwidthUpgradeNegotiationFrameRunnable : public Runnable {
|
||||
public:
|
||||
void run() override {}
|
||||
};
|
||||
|
||||
} // namespace base_bandwidth_upgrade_handler
|
||||
|
||||
BaseBandwidthUpgradeHandler::BaseBandwidthUpgradeHandler(
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager)
|
||||
: endpoint_channel_manager_(endpoint_channel_manager),
|
||||
alarm_executor_(nullptr),
|
||||
serial_executor_(nullptr),
|
||||
previous_endpoint_channels_(),
|
||||
in_progress_upgrades_(),
|
||||
safe_to_close_write_timestamps_() {}
|
||||
|
||||
BaseBandwidthUpgradeHandler::~BaseBandwidthUpgradeHandler() {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::revert() {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::initiateBandwidthUpgradeForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::processBandwidthUpgradeNegotiationFrame(
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame> bandwidth_upgrade_negotiation,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy, const string& from_endpoint_id,
|
||||
proto::connections::Medium current_medium) {}
|
||||
|
||||
Ptr<EndpointChannelManager>
|
||||
BaseBandwidthUpgradeHandler::getEndpointChannelManager() {
|
||||
return endpoint_channel_manager_;
|
||||
}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::onIncomingConnection(
|
||||
Ptr<IncomingSocketConnection> incoming_socket_connection) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::runOnBandwidthUpgradeHandlerThread(
|
||||
Ptr<Runnable> runnable) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::runUpgradeProtocol(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> new_endpoint_channel) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::processBandwidthUpgradePathAvailableEvent(
|
||||
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
|
||||
upgrade_path_info,
|
||||
proto::connections::Medium current_medium) {}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
BaseBandwidthUpgradeHandler::processBandwidthUpgradePathAvailableEventInternal(
|
||||
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
|
||||
upgrade_path_info) {
|
||||
return Ptr<EndpointChannel>();
|
||||
}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::processLastWriteToPriorChannelEvent(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
|
||||
|
||||
void BaseBandwidthUpgradeHandler::processSafeToClosePriorChannelEvent(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
|
||||
|
||||
std::int64_t BaseBandwidthUpgradeHandler::calculateCloseDelay(
|
||||
const string& endpoint_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int64_t BaseBandwidthUpgradeHandler::getMillisSinceSafeCloseWritten(
|
||||
const string& endpoint_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(ahlee): This will differ from the Java code as we don't have to handle
|
||||
// analytics in the C++ code.
|
||||
void BaseBandwidthUpgradeHandler::
|
||||
attemptToRecordBandwidthUpgradeErrorForUnknownEndpoint(
|
||||
proto::connections::BandwidthUpgradeResult result,
|
||||
proto::connections::BandwidthUpgradeErrorStage error_stage) {}
|
||||
|
||||
// TODO(ahlee): This will differ from the Java code (previously threw an
|
||||
// UpgradeException).
|
||||
Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>
|
||||
BaseBandwidthUpgradeHandler::readClientIntroductionFrame(
|
||||
Ptr<EndpointChannel> endpoint_channel) {
|
||||
return Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>();
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,181 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_BASE_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
#define CORE_INTERNAL_BASE_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
#include "core/internal/bandwidth_upgrade_handler.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/api/count_down_latch.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace base_bandwidth_upgrade_handler {
|
||||
|
||||
class RevertRunnable;
|
||||
class InitiateBandwidthUpgradeForEndpointRunnable;
|
||||
class ProcessEndpointDisconnectionRunnable;
|
||||
class ProcessBandwidthUpgradeNegotiationFrameRunnable;
|
||||
|
||||
} // namespace base_bandwidth_upgrade_handler
|
||||
|
||||
// Base class for managing the upgrade of endpoints to a different medium for
|
||||
// communication (from whatever they were previously using).
|
||||
//
|
||||
// <p>The sequencing of the upgrade protocol is as follows:
|
||||
// <ul>
|
||||
// <li>Initiator sets up an upgrade path, sends
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.UPGRADE_PATH_AVAILABLE to Responder over
|
||||
// the prior EndpointChannel.
|
||||
// <li>Responder joins the upgrade path, sends (without encryption)
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.CLIENT_INTRODUCTION over the new
|
||||
// EndpointChannel, and sends
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL over the
|
||||
// prior EndpointChannel.
|
||||
// <li>Initiator receives BANDWIDTH_UPGRADE_NEGOTIATION.CLIENT_INTRODUCTION
|
||||
// over the newly-established EndpointChannel, and sends
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL over the
|
||||
// prior EndpointChannel.
|
||||
// <li>Both wait to receive
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL from the
|
||||
// other, and upon doing so, send
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL to each other
|
||||
// <li>Both then wait to receive
|
||||
// BANDWIDTH_UPGRADE_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL from the
|
||||
// other, and upon doing so, close the prior EndpointChannel.
|
||||
// </ul>
|
||||
class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
|
||||
explicit BaseBandwidthUpgradeHandler(
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager);
|
||||
~BaseBandwidthUpgradeHandler() override;
|
||||
|
||||
void revert() override;
|
||||
void processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) override;
|
||||
// Initiates the bandwidth upgrade and sends an UPGRADE_PATH_AVAILABLE
|
||||
// OfflineFrame.
|
||||
void initiateBandwidthUpgradeForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id) override;
|
||||
void processBandwidthUpgradeNegotiationFrame(
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame> bandwidth_upgrade_negotiation,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
const string& from_endpoint_id,
|
||||
proto::connections::Medium current_medium) override;
|
||||
|
||||
protected:
|
||||
// Represents the incoming Socket the Initiator has gotten after initializing
|
||||
// its upgraded bandwidth medium.
|
||||
class IncomingSocketConnection {
|
||||
public:
|
||||
virtual ~IncomingSocketConnection() {}
|
||||
|
||||
virtual string socketToString() = 0;
|
||||
virtual void closeSocket() = 0;
|
||||
// TODO(ahlee): Make sure to be careful with the ownership story of this.
|
||||
// Leaning towards releasing to the caller.
|
||||
virtual Ptr<EndpointChannel> getEndpointChannel() = 0;
|
||||
};
|
||||
|
||||
// Called by the Initiator to setup the upgraded medium for this endpoint (if
|
||||
// that hasn't already been done), and returns a serialized UpgradePathInfo
|
||||
// that can be sent to the Responder.
|
||||
// TODO(ahlee): This will differ from the Java code (previously threw an
|
||||
// UpgradeException). Leaving the return type simple for the skeleton - I'll
|
||||
// switch to a pair if the result enum is needed.
|
||||
// @BandwidthUpgradeHandlerThread
|
||||
virtual ConstPtr<ByteArray> initializeUpgradedMediumForEndpoint(
|
||||
const string& endpoint_id) = 0;
|
||||
// Called to revert any state changed by the Initiator to setup the upgraded
|
||||
// medium for an endpoint.
|
||||
// @BandwidthUpgradeHandlerThread
|
||||
virtual void revertImpl() = 0;
|
||||
// Called by the Responder to setup the upgraded medium for this endpoint (if
|
||||
// that hasn't already been done) using the UpgradePathInfo sent by the
|
||||
// Initiator, and returns a new EndpointChannel for the upgraded medium.
|
||||
// @BandwidthUpgradeHandlerThread
|
||||
// TODO(ahlee): This will differ from the Java code (previously threw an
|
||||
// exception).
|
||||
virtual Ptr<EndpointChannel> createUpgradedEndpointChannel(
|
||||
const string& endpoint_id,
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
|
||||
upgrade_path_info) = 0;
|
||||
// Returns the upgrade medium of the BandwidthUpgradeHandler.
|
||||
// @BandwidthUpgradeHandlerThread
|
||||
virtual proto::connections::Medium getUpgradeMedium() = 0;
|
||||
|
||||
Ptr<EndpointChannelManager> getEndpointChannelManager();
|
||||
// Common functionality to take an incoming connection and go through the
|
||||
// upgrade process.
|
||||
// @BandwidthUpgradeHandlerThread
|
||||
void onIncomingConnection(
|
||||
Ptr<IncomingSocketConnection> incoming_socket_connection);
|
||||
void runOnBandwidthUpgradeHandlerThread(Ptr<Runnable> runnable);
|
||||
|
||||
private:
|
||||
friend class base_bandwidth_upgrade_handler::RevertRunnable;
|
||||
friend class base_bandwidth_upgrade_handler::
|
||||
InitiateBandwidthUpgradeForEndpointRunnable;
|
||||
friend class base_bandwidth_upgrade_handler::
|
||||
ProcessEndpointDisconnectionRunnable;
|
||||
friend class base_bandwidth_upgrade_handler::
|
||||
ProcessBandwidthUpgradeNegotiationFrameRunnable;
|
||||
|
||||
void runUpgradeProtocol(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> new_endpoint_channel);
|
||||
void processBandwidthUpgradePathAvailableEvent(
|
||||
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
|
||||
upgrade_path_info,
|
||||
proto::connections::Medium current_medium);
|
||||
Ptr<EndpointChannel> processBandwidthUpgradePathAvailableEventInternal(
|
||||
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
|
||||
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
|
||||
upgrade_path_info);
|
||||
void processLastWriteToPriorChannelEvent(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id);
|
||||
void processSafeToClosePriorChannelEvent(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id);
|
||||
std::int64_t calculateCloseDelay(const string& endpoint_id);
|
||||
std::int64_t getMillisSinceSafeCloseWritten(const string& endpoint_id);
|
||||
void attemptToRecordBandwidthUpgradeErrorForUnknownEndpoint(
|
||||
proto::connections::BandwidthUpgradeResult result,
|
||||
proto::connections::BandwidthUpgradeErrorStage error_stage);
|
||||
Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>
|
||||
readClientIntroductionFrame(Ptr<EndpointChannel> endpoint_channel);
|
||||
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager_;
|
||||
ScopedPtr<Ptr<typename Platform::ScheduledExecutorType> > alarm_executor_;
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType> > serial_executor_;
|
||||
// Stores each upgraded endpoint's previous EndpointChannel (that was
|
||||
// displaced in favor of a new EndpointChannel) temporarily, until it can
|
||||
// safely be shut down for good in processLastWriteToPriorChannelEvent().
|
||||
typedef std::map<string, Ptr<EndpointChannel> > PreviousEndpointChannelsMap;
|
||||
PreviousEndpointChannelsMap previous_endpoint_channels_;
|
||||
// Maps endpointId -> ClientProxy for which
|
||||
// initiateBandwidthUpgradeForEndpoint() has been called but which have not
|
||||
// yet completed the upgrade via onIncomingConnection().
|
||||
typedef std::map<string, Ptr<ClientProxy<Platform> > > InProgressUpgradesMap;
|
||||
InProgressUpgradesMap in_progress_upgrades_;
|
||||
// Maps endpointId -> timestamp of when the SAFE_TO_CLOSE message was written.
|
||||
typedef std::map<string, std::int64_t> SafeToCloseWriteTimestampsMap;
|
||||
SafeToCloseWriteTimestampsMap safe_to_close_write_timestamps_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_BASE_BANDWIDTH_UPGRADE_HANDLER_H_
|
||||
@@ -1,18 +1,18 @@
|
||||
#ifndef CORE_V2_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
#define CORE_V2_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
#ifndef CORE_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
#define CORE_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "core_v2/internal/bwu_handler.h"
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/bwu_handler.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform_v2/public/cancelable_alarm.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/scheduled_executor.h"
|
||||
#include "platform_v2/public/single_thread_executor.h"
|
||||
#include "platform/public/cancelable_alarm.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/scheduled_executor.h"
|
||||
#include "platform/public/single_thread_executor.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/time/clock.h"
|
||||
@@ -45,4 +45,4 @@ class BaseBwuHandler : public BwuHandler {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
#endif // CORE_INTERNAL_BASE_BWU_HANDLER_H_
|
||||
@@ -2,9 +2,15 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/synchronized.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -12,10 +18,8 @@ namespace connections {
|
||||
|
||||
namespace {
|
||||
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
|
||||
std::int32_t bytesToInt(ConstPtr<ByteArray> bytes) {
|
||||
const char* int_bytes = bytes->getData();
|
||||
std::int32_t BytesToInt(const ByteArray& bytes) {
|
||||
const char* int_bytes = bytes.data();
|
||||
|
||||
std::int32_t result = 0;
|
||||
result |= (static_cast<std::int32_t>(int_bytes[0]) & 0x0FF) << 24;
|
||||
@@ -26,308 +30,264 @@ std::int32_t bytesToInt(ConstPtr<ByteArray> bytes) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> intToBytes(std::int32_t value) {
|
||||
ByteArray IntToBytes(std::int32_t value) {
|
||||
char int_bytes[sizeof(std::int32_t)];
|
||||
int_bytes[0] = static_cast<char>((value >> 24) & 0x0FF);
|
||||
int_bytes[1] = static_cast<char>((value >> 16) & 0x0FF);
|
||||
int_bytes[2] = static_cast<char>((value >> 8) & 0x0FF);
|
||||
int_bytes[3] = static_cast<char>((value)&0x0FF);
|
||||
|
||||
return MakeConstPtr(new ByteArray(int_bytes, sizeof(int_bytes)));
|
||||
return ByteArray(int_bytes, sizeof(int_bytes));
|
||||
}
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> readExactly(Ptr<InputStream> reader,
|
||||
std::int64_t size) {
|
||||
string buffer;
|
||||
std::int64_t remaining_size = size;
|
||||
ExceptionOr<ByteArray> ReadExactly(InputStream* reader, std::int64_t size) {
|
||||
ByteArray buffer(size);
|
||||
std::int64_t current_pos = 0;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
ExceptionOr<ConstPtr<ByteArray>> read_bytes = reader->read(remaining_size);
|
||||
while (current_pos < size) {
|
||||
ExceptionOr<ByteArray> read_bytes = reader->Read(size - current_pos);
|
||||
if (!read_bytes.ok()) {
|
||||
if (Exception::IO == read_bytes.exception()) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes.exception());
|
||||
}
|
||||
return read_bytes;
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_read_bytes(read_bytes.result());
|
||||
ByteArray result = read_bytes.result();
|
||||
|
||||
// In Java, EOFException is a sub-variant of IOException.
|
||||
if (scoped_read_bytes.isNull() || scoped_read_bytes->size() == 0) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
|
||||
if (result.Empty()) {
|
||||
return ExceptionOr<ByteArray>(Exception::kIo);
|
||||
}
|
||||
|
||||
buffer.append(scoped_read_bytes->getData(), scoped_read_bytes->size());
|
||||
remaining_size -= scoped_read_bytes->size();
|
||||
buffer.CopyAt(current_pos, result);
|
||||
current_pos += result.size();
|
||||
}
|
||||
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(
|
||||
MakeConstPtr(new ByteArray(buffer.data(), buffer.size())));
|
||||
return ExceptionOr<ByteArray>(std::move(buffer));
|
||||
}
|
||||
|
||||
ExceptionOr<std::int32_t> readInt(Ptr<InputStream> reader) {
|
||||
ExceptionOr<ConstPtr<ByteArray>> read_bytes =
|
||||
readExactly(reader, sizeof(std::int32_t));
|
||||
ExceptionOr<std::int32_t> ReadInt(InputStream* reader) {
|
||||
ExceptionOr<ByteArray> read_bytes = ReadExactly(reader, sizeof(std::int32_t));
|
||||
if (!read_bytes.ok()) {
|
||||
if (Exception::IO == read_bytes.exception()) {
|
||||
return ExceptionOr<std::int32_t>(read_bytes.exception());
|
||||
}
|
||||
return ExceptionOr<std::int32_t>(read_bytes.exception());
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_read_bytes(read_bytes.result());
|
||||
|
||||
return ExceptionOr<std::int32_t>(bytesToInt(scoped_read_bytes.get()));
|
||||
return ExceptionOr<std::int32_t>(BytesToInt(std::move(read_bytes.result())));
|
||||
}
|
||||
|
||||
Exception::Value writeInt(Ptr<OutputStream> writer, std::int32_t value) {
|
||||
return writer->write(intToBytes(value));
|
||||
Exception WriteInt(OutputStream* writer, std::int32_t value) {
|
||||
return writer->Write(IntToBytes(value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(b/150763574): Move implementatiopn to header or .inc file.
|
||||
BaseEndpointChannel::BaseEndpointChannel(absl::string_view channel_name,
|
||||
Ptr<InputStream> reader,
|
||||
Ptr<OutputStream> writer)
|
||||
: last_read_timestamp_(-1),
|
||||
channel_name_(channel_name),
|
||||
system_clock_(Platform::createSystemClock()),
|
||||
reader_lock_(Platform::createLock()),
|
||||
reader_(reader),
|
||||
writer_lock_(Platform::createLock()),
|
||||
writer_(writer),
|
||||
encryption_context_(Platform::createAtomicReference(
|
||||
Ptr<securegcm::D2DConnectionContextV1>())),
|
||||
is_paused_lock_(Platform::createLock()),
|
||||
is_paused_condition_variable_(
|
||||
Platform::createConditionVariable(is_paused_lock_.get())),
|
||||
is_paused_(Platform::createAtomicBoolean(false)) {}
|
||||
BaseEndpointChannel::BaseEndpointChannel(const std::string& channel_name,
|
||||
InputStream* reader,
|
||||
OutputStream* writer)
|
||||
: channel_name_(channel_name), reader_(reader), writer_(writer) {}
|
||||
|
||||
BaseEndpointChannel::~BaseEndpointChannel() {
|
||||
// WARNING: Make sure to never access reader_ and writer_ from here.
|
||||
//
|
||||
// They're owned by the specialized *Socket classes that are in turn
|
||||
// owned by the *EndpointChannel children of this class, so by this point,
|
||||
// they've been destroyed and now point to invalid memory.
|
||||
//
|
||||
// "Ugh!" is right -- this won't be a problem once we have a standardized
|
||||
// Socket interface we can hold up in this class (instead of holding
|
||||
// specialized implementations of that hypothetical interface in each child
|
||||
// of this class).
|
||||
ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
|
||||
ByteArray result;
|
||||
{
|
||||
MutexLock lock(&reader_mutex_);
|
||||
|
||||
ExceptionOr<std::int32_t> read_int = ReadInt(reader_);
|
||||
if (!read_int.ok()) {
|
||||
return ExceptionOr<ByteArray>(read_int.exception());
|
||||
}
|
||||
|
||||
if (read_int.result() < 0 || read_int.result() > kMaxAllowedReadBytes) {
|
||||
return ExceptionOr<ByteArray>(Exception::kIo);
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> read_bytes = ReadExactly(reader_, read_int.result());
|
||||
if (!read_bytes.ok()) {
|
||||
return read_bytes;
|
||||
}
|
||||
result = std::move(read_bytes.result());
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock crypto_lock(&crypto_mutex_);
|
||||
if (IsEncryptionEnabledLocked()) {
|
||||
// If encryption is enabled, decode the message.
|
||||
std::string input(std::move(result));
|
||||
std::unique_ptr<std::string> decrypted_data =
|
||||
crypto_context_->DecodeMessageFromPeer(input);
|
||||
if (decrypted_data) {
|
||||
result = ByteArray(std::move(*decrypted_data));
|
||||
} else {
|
||||
// It could be a protocol race, where remote party sends a KEEP_ALIVE
|
||||
// before encryption is setup on their side, and we receive it after
|
||||
// we switched to encryption mode.
|
||||
// In this case, we verify that message is indeed a valid KEEP_ALIVE,
|
||||
// and let it through if it is, otherwise message is erased.
|
||||
// TODO(apolyudov): verify this happens at most once per session.
|
||||
result = {};
|
||||
auto parsed = parser::FromBytes(ByteArray(input));
|
||||
if (parsed.ok() &&
|
||||
parser::GetFrameType(parsed.result()) == V1Frame::KEEP_ALIVE) {
|
||||
result = ByteArray(input);
|
||||
}
|
||||
}
|
||||
if (result.Empty()) {
|
||||
return ExceptionOr<ByteArray>(Exception::kInvalidProtocolBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock lock(&last_read_mutex_);
|
||||
last_read_timestamp_ = SystemClock::ElapsedRealtime();
|
||||
}
|
||||
return ExceptionOr<ByteArray>(result);
|
||||
}
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> BaseEndpointChannel::read() {
|
||||
Synchronized s(reader_lock_.get());
|
||||
|
||||
ExceptionOr<std::int32_t> read_int = readInt(reader_);
|
||||
if (!read_int.ok()) {
|
||||
if (Exception::IO == read_int.exception()) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(read_int.exception());
|
||||
Exception BaseEndpointChannel::Write(const ByteArray& data) {
|
||||
{
|
||||
MutexLock pause_lock(&is_paused_mutex_);
|
||||
if (is_paused_) {
|
||||
BlockUntilUnpaused();
|
||||
}
|
||||
}
|
||||
|
||||
if (read_int.result() < 0) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
|
||||
} else if (read_int.result() > kMaxAllowedReadBytes) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
|
||||
}
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> read_bytes =
|
||||
readExactly(reader_, read_int.result());
|
||||
if (!read_bytes.ok()) {
|
||||
if (Exception::IO == read_bytes.exception()) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes.exception());
|
||||
ByteArray encrypted_data;
|
||||
const ByteArray* data_to_write = &data;
|
||||
{
|
||||
MutexLock crypto_lock(&crypto_mutex_);
|
||||
if (IsEncryptionEnabledLocked()) {
|
||||
// If encryption is enabled, encode the message.
|
||||
std::unique_ptr<std::string> encrypted =
|
||||
crypto_context_->EncodeMessageToPeer(std::string(data));
|
||||
if (!encrypted) return {Exception::kIo};
|
||||
encrypted_data = ByteArray(std::move(*encrypted));
|
||||
data_to_write = &encrypted_data;
|
||||
}
|
||||
}
|
||||
|
||||
// This should be ScopedPtr usually, but because of the unique requirement of
|
||||
// reassigning this variable when encryption is enabled, we can't make use of
|
||||
// the power of ScopedPtr, and instead have to do manual memory management.
|
||||
ConstPtr<ByteArray> read_bytes_result = read_bytes.result();
|
||||
|
||||
// If encryption is enabled, decode the message.
|
||||
if (isEncryptionEnabled()) {
|
||||
std::unique_ptr<string> decoded_bytes =
|
||||
encryption_context_->get()->DecodeMessageFromPeer(
|
||||
string(read_bytes_result->getData(), read_bytes_result->size()));
|
||||
// Now that we are done using read_bytes_result, we should unconditionally
|
||||
// destroy it, because we either reassign to the value of decoded_bytes, or
|
||||
// short-circuit out of here on error.
|
||||
read_bytes_result.destroy();
|
||||
if (decoded_bytes == nullptr) {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(
|
||||
Exception::INVALID_PROTOCOL_BUFFER);
|
||||
}
|
||||
read_bytes_result = MakeConstPtr(
|
||||
new ByteArray(decoded_bytes->data(), decoded_bytes->size()));
|
||||
}
|
||||
|
||||
last_read_timestamp_ = system_clock_->elapsedRealtime();
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes_result);
|
||||
}
|
||||
|
||||
Exception::Value BaseEndpointChannel::write(ConstPtr<ByteArray> data) {
|
||||
Synchronized s(writer_lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
|
||||
|
||||
if (isPaused()) {
|
||||
blockUntilUnpaused();
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> data_to_write;
|
||||
// If encryption is enabled, encode the message.
|
||||
if (isEncryptionEnabled()) {
|
||||
std::unique_ptr<string> message =
|
||||
encryption_context_->get()->EncodeMessageToPeer(
|
||||
string(scoped_data->getData(), scoped_data->size()));
|
||||
assert(message != nullptr);
|
||||
data_to_write =
|
||||
MakeConstPtr(new ByteArray(message->data(), message->size()));
|
||||
} else {
|
||||
// Else, just make data_to_write point to the passed-in data.
|
||||
data_to_write = scoped_data.release();
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_data_to_write(data_to_write);
|
||||
|
||||
Exception::Value write_exception = writeInt(
|
||||
writer_, static_cast<std::int32_t>(scoped_data_to_write->size()));
|
||||
if (Exception::NONE != write_exception) {
|
||||
if (Exception::IO == write_exception) {
|
||||
{
|
||||
MutexLock lock(&writer_mutex_);
|
||||
Exception write_exception =
|
||||
WriteInt(writer_, static_cast<std::int32_t>(data_to_write->size()));
|
||||
if (write_exception.Raised()) {
|
||||
return write_exception;
|
||||
}
|
||||
}
|
||||
|
||||
write_exception = writer_->write(scoped_data_to_write.release());
|
||||
if (Exception::NONE != write_exception) {
|
||||
if (Exception::IO == write_exception) {
|
||||
write_exception = writer_->Write(*data_to_write);
|
||||
if (write_exception.Raised()) {
|
||||
return write_exception;
|
||||
}
|
||||
}
|
||||
|
||||
Exception::Value flush_exception = writer_->flush();
|
||||
if (Exception::NONE != flush_exception) {
|
||||
if (Exception::IO == flush_exception) {
|
||||
Exception flush_exception = writer_->Flush();
|
||||
if (flush_exception.Raised()) {
|
||||
return flush_exception;
|
||||
}
|
||||
}
|
||||
|
||||
return Exception::NONE;
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::close() {
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// This block deviates from the corresponding Java code.
|
||||
//
|
||||
// In the corresponding Java code, close() calls
|
||||
// close(proto::connections::DisconnectionReason) while here we do the
|
||||
// opposite. This is because proto::connections::DisconnectionReason can be
|
||||
// null in Java but not in C++.
|
||||
Exception::Value reader_close_exception = reader_->close();
|
||||
if (Exception::NONE != reader_close_exception) {
|
||||
if (Exception::IO == reader_close_exception) {
|
||||
void BaseEndpointChannel::Close() {
|
||||
{
|
||||
// In case channel is paused, resume it first thing.
|
||||
MutexLock lock(&is_paused_mutex_);
|
||||
UnblockPausedWriter();
|
||||
}
|
||||
CloseIo();
|
||||
CloseImpl();
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::CloseIo() {
|
||||
// Keep this method dedicated to reader and writer handling an nothing else.
|
||||
{
|
||||
// Do not take reader_mutex_ here: read may be in progress, and it will
|
||||
// deadlock. Calling Close() with Read() in progress will terminate the
|
||||
// IO and Read() will proceed normally (with Exception::kIo).
|
||||
Exception exception = reader_->Close();
|
||||
if (!exception.Ok()) {
|
||||
// Add logging.
|
||||
}
|
||||
}
|
||||
Exception::Value writer_close_exception = writer_->close();
|
||||
if (Exception::NONE != writer_close_exception) {
|
||||
if (Exception::IO == writer_close_exception) {
|
||||
{
|
||||
// Do not take writer_mutex_ here: write may be in progress, and it will
|
||||
// deadlock. Calling Close() with Write() in progress will terminate the
|
||||
// IO and Write() will proceed normally (with Exception::kIo).
|
||||
Exception exception = writer_->Close();
|
||||
if (!exception.Ok()) {
|
||||
// Add logging.
|
||||
}
|
||||
}
|
||||
|
||||
closeImpl();
|
||||
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::close(
|
||||
void BaseEndpointChannel::Close(
|
||||
proto::connections::DisconnectionReason reason) {
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// This block deviates from the corresponding Java code.
|
||||
// Look at the corresponding block in the close() method above for details on
|
||||
// the deviation.
|
||||
close();
|
||||
|
||||
// TODO(tracyzhou): Add logging.
|
||||
Close();
|
||||
}
|
||||
|
||||
string BaseEndpointChannel::getType() {
|
||||
string subtype = isEncryptionEnabled() ? "ENCRYPTED_" : "";
|
||||
switch (getMedium()) {
|
||||
std::string BaseEndpointChannel::GetType() const {
|
||||
MutexLock crypto_lock(&crypto_mutex_);
|
||||
std::string subtype = IsEncryptionEnabledLocked() ? "ENCRYPTED_" : "";
|
||||
|
||||
switch (GetMedium()) {
|
||||
case proto::connections::Medium::BLUETOOTH:
|
||||
return subtype + "BLUETOOTH";
|
||||
return absl::StrCat(subtype, "BLUETOOTH");
|
||||
case proto::connections::Medium::BLE:
|
||||
return subtype + "BLE";
|
||||
return absl::StrCat(subtype, "BLE");
|
||||
case proto::connections::Medium::MDNS:
|
||||
return subtype + "MDNS";
|
||||
return absl::StrCat(subtype, "MDNS");
|
||||
case proto::connections::Medium::WIFI_HOTSPOT:
|
||||
return subtype + "WIFI_HOTSPOT";
|
||||
return absl::StrCat(subtype, "WIFI_HOTSPOT");
|
||||
case proto::connections::Medium::WIFI_LAN:
|
||||
return subtype + "WIFI_LAN";
|
||||
return absl::StrCat(subtype, "WIFI_LAN");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
string BaseEndpointChannel::getName() { return channel_name_; }
|
||||
std::string BaseEndpointChannel::GetName() const { return channel_name_; }
|
||||
|
||||
void BaseEndpointChannel::enableEncryption(
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
|
||||
assert(!encryption_context.isNull());
|
||||
encryption_context_->set(encryption_context);
|
||||
void BaseEndpointChannel::EnableEncryption(
|
||||
std::shared_ptr<EncryptionContext> context) {
|
||||
MutexLock crypto_lock(&crypto_mutex_);
|
||||
crypto_context_ = context;
|
||||
}
|
||||
|
||||
bool BaseEndpointChannel::isPaused() { return is_paused_->get(); }
|
||||
|
||||
void BaseEndpointChannel::pause() { is_paused_->set(true); }
|
||||
|
||||
void BaseEndpointChannel::resume() {
|
||||
is_paused_->set(false);
|
||||
unblockPausedWriter();
|
||||
bool BaseEndpointChannel::IsPaused() const {
|
||||
MutexLock lock(&is_paused_mutex_);
|
||||
return is_paused_;
|
||||
}
|
||||
|
||||
std::int64_t BaseEndpointChannel::getLastReadTimestamp() {
|
||||
void BaseEndpointChannel::Pause() {
|
||||
MutexLock lock(&is_paused_mutex_);
|
||||
is_paused_ = true;
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::Resume() {
|
||||
MutexLock lock(&is_paused_mutex_);
|
||||
is_paused_ = false;
|
||||
is_paused_cond_.Notify();
|
||||
}
|
||||
|
||||
absl::Time BaseEndpointChannel::GetLastReadTimestamp() const {
|
||||
MutexLock lock(&last_read_mutex_);
|
||||
return last_read_timestamp_;
|
||||
}
|
||||
|
||||
bool BaseEndpointChannel::isEncryptionEnabled() {
|
||||
return !encryption_context_->get().isNull();
|
||||
bool BaseEndpointChannel::IsEncryptionEnabledLocked() const {
|
||||
return crypto_context_ != nullptr;
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::unblockPausedWriter() {
|
||||
Synchronized s(is_paused_lock_.get());
|
||||
|
||||
// Notify to tell the thread calling wait() to check again.
|
||||
// NOTE: There is only ever one thread blocked by wait() at a time, because
|
||||
// EndpointChannel.write(Ptr<ByteArray>) is synchronized on writer. That means
|
||||
// the first thread to call write(byte[]) will be blocked via
|
||||
// blockUntilUnpaused() and all future threads will be blocked via
|
||||
// synchronized(writer_lock_).
|
||||
is_paused_condition_variable_->notify();
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::blockUntilUnpaused() {
|
||||
Synchronized s(is_paused_lock_.get());
|
||||
|
||||
void BaseEndpointChannel::BlockUntilUnpaused() {
|
||||
// For more on how this works, see
|
||||
// https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
|
||||
while (is_paused_->get()) {
|
||||
Exception::Value wait_succeeded = is_paused_condition_variable_->wait();
|
||||
if (Exception::NONE != wait_succeeded) {
|
||||
if (Exception::INTERRUPTED == wait_succeeded) {
|
||||
// If we were interrupted, pass the interrupt up the stack and then exit
|
||||
// immediately.
|
||||
// Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
while (is_paused_) {
|
||||
Exception wait_succeeded = is_paused_cond_.Wait();
|
||||
if (!wait_succeeded.Ok()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseEndpointChannel::UnblockPausedWriter() {
|
||||
// For more on how this works, see
|
||||
// https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
|
||||
is_paused_ = false;
|
||||
is_paused_cond_.Notify();
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
#define CORE_INTERNAL_BASE_ENDPOINT_CHANNEL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "platform/api/atomic_boolean.h"
|
||||
#include "platform/api/atomic_reference.h"
|
||||
#include "platform/api/condition_variable.h"
|
||||
#include "platform/api/input_stream.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/api/output_stream.h"
|
||||
#include "platform/api/system_clock.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/input_stream.h"
|
||||
#include "platform/base/output_stream.h"
|
||||
#include "platform/public/atomic_reference.h"
|
||||
#include "platform/public/condition_variable.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "securegcm/d2d_connection_context_v1.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/base/thread_annotations.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -24,83 +23,88 @@ namespace connections {
|
||||
|
||||
class BaseEndpointChannel : public EndpointChannel {
|
||||
public:
|
||||
BaseEndpointChannel(absl::string_view channel_name, Ptr<InputStream> reader,
|
||||
Ptr<OutputStream> writer);
|
||||
~BaseEndpointChannel() override;
|
||||
BaseEndpointChannel(const std::string& channel_name, InputStream* reader,
|
||||
OutputStream* writer);
|
||||
~BaseEndpointChannel() override = default;
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray> > read() override;
|
||||
ExceptionOr<ByteArray> Read()
|
||||
ABSL_LOCKS_EXCLUDED(reader_mutex_, crypto_mutex_,
|
||||
last_read_mutex_) override;
|
||||
|
||||
Exception::Value write(ConstPtr<ByteArray> data) override;
|
||||
Exception Write(const ByteArray& data)
|
||||
ABSL_LOCKS_EXCLUDED(writer_mutex_, crypto_mutex_) override;
|
||||
|
||||
// Closes this EndpointChannel, without tracking the closure in analytics.
|
||||
void close() override;
|
||||
void Close() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
|
||||
|
||||
// Closes this EndpointChannel and records the closure with the given reason.
|
||||
void close(proto::connections::DisconnectionReason reason) override;
|
||||
void Close(proto::connections::DisconnectionReason reason) override;
|
||||
|
||||
// Returns a one-word type descriptor for the concrete EndpointChannel
|
||||
// implementation that can be used in log messages; eg: BLUETOOTH, BLE,
|
||||
// WIFI.
|
||||
string getType() override;
|
||||
std::string GetType() const override;
|
||||
|
||||
// Returns the name of the EndpointChannel.
|
||||
string getName() override;
|
||||
std::string GetName() const override;
|
||||
|
||||
// Enables encryption on the EndpointChannel.
|
||||
void enableEncryption(
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context) override;
|
||||
// Should be called after connection is accepted by both parties, and
|
||||
// before entering data phase, where Payloads may be exchanged.
|
||||
void EnableEncryption(std::shared_ptr<EncryptionContext> context) override;
|
||||
|
||||
// True if the EndpointChannel is currently pausing all writes.
|
||||
bool isPaused() override;
|
||||
bool IsPaused() const ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
|
||||
|
||||
// Pauses all writes on this EndpointChannel until resume() is called.
|
||||
void pause() override;
|
||||
void Pause() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
|
||||
|
||||
// Resumes any writes on this EndpointChannel that were suspended when pause()
|
||||
// was called.
|
||||
void resume() override;
|
||||
void Resume() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
|
||||
|
||||
// Returns the timestamp (in elapsedRealtime) of the last read from this
|
||||
// endpoint, or -1 if no reads have occurred.
|
||||
std::int64_t getLastReadTimestamp() override;
|
||||
// Returns the timestamp (returned by ElapsedRealtime) of the last read from
|
||||
// this endpoint, or -1 if no reads have occurred.
|
||||
absl::Time GetLastReadTimestamp() const
|
||||
ABSL_LOCKS_EXCLUDED(last_read_mutex_) override;
|
||||
|
||||
protected:
|
||||
virtual void closeImpl() = 0;
|
||||
virtual void CloseImpl() = 0;
|
||||
|
||||
private:
|
||||
// Used to sanity check that our frame sizes are reasonable.
|
||||
static constexpr std::int32_t kMaxAllowedReadBytes = 1048576; // 1MB
|
||||
|
||||
bool isEncryptionEnabled();
|
||||
void unblockPausedWriter();
|
||||
void blockUntilUnpaused();
|
||||
bool IsEncryptionEnabledLocked() const
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(crypto_mutex_);
|
||||
void UnblockPausedWriter() ABSL_EXCLUSIVE_LOCKS_REQUIRED(is_paused_mutex_);
|
||||
void BlockUntilUnpaused() ABSL_EXCLUSIVE_LOCKS_REQUIRED(is_paused_mutex_);
|
||||
void CloseIo() ABSL_NO_THREAD_SAFETY_ANALYSIS;
|
||||
|
||||
volatile std::int64_t last_read_timestamp_;
|
||||
|
||||
const string channel_name_;
|
||||
|
||||
ScopedPtr<Ptr<SystemClock> > system_clock_;
|
||||
// We need a separate mutex to pritect read timestamp, because if a read
|
||||
// blocks on IO, we don't want timestamp read access to block too.
|
||||
mutable Mutex last_read_mutex_;
|
||||
absl::Time last_read_timestamp_ ABSL_GUARDED_BY(last_read_mutex_) =
|
||||
absl::InfinitePast();
|
||||
const std::string channel_name_;
|
||||
|
||||
// The reader and writer are synchronized independently since we can't have
|
||||
// writes waiting on reads that might potentially block forever.
|
||||
ScopedPtr<Ptr<Lock> > reader_lock_;
|
||||
// Not owned by this class, see the note in the destructor for a special
|
||||
// restriction on usage.
|
||||
Ptr<InputStream> reader_;
|
||||
Mutex reader_mutex_;
|
||||
InputStream* reader_ ABSL_PT_GUARDED_BY(reader_mutex_);
|
||||
|
||||
ScopedPtr<Ptr<Lock> > writer_lock_;
|
||||
// Not owned by this class, see the note in the destructor for a special
|
||||
// restriction on usage.
|
||||
Ptr<OutputStream> writer_;
|
||||
Mutex writer_mutex_;
|
||||
OutputStream* writer_ ABSL_PT_GUARDED_BY(writer_mutex_);
|
||||
|
||||
// An encryptor/decryptor. May be null.
|
||||
ScopedPtr<Ptr<AtomicReference<Ptr<securegcm::D2DConnectionContextV1> > > >
|
||||
encryption_context_;
|
||||
mutable Mutex crypto_mutex_;
|
||||
std::shared_ptr<EncryptionContext> crypto_context_
|
||||
ABSL_GUARDED_BY(crypto_mutex_) ABSL_PT_GUARDED_BY(crypto_mutex_);
|
||||
|
||||
ScopedPtr<Ptr<Lock> > is_paused_lock_;
|
||||
ScopedPtr<Ptr<ConditionVariable> > is_paused_condition_variable_;
|
||||
mutable Mutex is_paused_mutex_;
|
||||
ConditionVariable is_paused_cond_{&is_paused_mutex_};
|
||||
// If true, writes should block until this has been set to false.
|
||||
ScopedPtr<Ptr<AtomicBoolean> > is_paused_;
|
||||
bool is_paused_ ABSL_GUARDED_BY(is_paused_mutex_) = false;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,43 +1,339 @@
|
||||
#include "core/internal/base_endpoint_channel.h"
|
||||
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/pipe.h"
|
||||
#include <utility>
|
||||
|
||||
#include "core/internal/encryption_runner.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/input_stream.h"
|
||||
#include "platform/base/output_stream.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/multi_thread_executor.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "platform/public/single_thread_executor.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "securegcm/d2d_connection_context_v1.h"
|
||||
#include "securegcm/ukey2_handshake.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace {
|
||||
|
||||
using ::location::nearby::proto::connections::DisconnectionReason;
|
||||
using ::location::nearby::proto::connections::Medium;
|
||||
using EncryptionContext = BaseEndpointChannel::EncryptionContext;
|
||||
|
||||
class TestEndpointChannel : public BaseEndpointChannel {
|
||||
public:
|
||||
explicit TestEndpointChannel(Ptr<InputStream> input_stream)
|
||||
: BaseEndpointChannel("channel", input_stream, Ptr<OutputStream>()) {}
|
||||
explicit TestEndpointChannel(InputStream* input, OutputStream* output)
|
||||
: BaseEndpointChannel("channel", input, output) {}
|
||||
|
||||
MOCK_METHOD(proto::connections::Medium, getMedium, (), (override));
|
||||
MOCK_METHOD(void, closeImpl, (), (override));
|
||||
MOCK_METHOD(Medium, GetMedium, (), (const override));
|
||||
MOCK_METHOD(void, CloseImpl, (), (override));
|
||||
};
|
||||
|
||||
using SamplePipe = Pipe;
|
||||
std::function<void()> MakeDataPump(
|
||||
std::string label, InputStream* input, OutputStream* output,
|
||||
std::function<void(const ByteArray&)> monitor = nullptr) {
|
||||
return [label, input, output, monitor]() {
|
||||
NEARBY_LOG(INFO, "streaming data thorough '%s'", label.c_str());
|
||||
while (true) {
|
||||
auto read_response = input->Read(Pipe::kChunkSize);
|
||||
if (!read_response.ok()) {
|
||||
NEARBY_LOG(INFO, "Peer reader closed on '%s'", label.c_str());
|
||||
output->Close();
|
||||
break;
|
||||
}
|
||||
if (monitor) {
|
||||
monitor(read_response.result());
|
||||
}
|
||||
auto write_response = output->Write(read_response.result());
|
||||
if (write_response.Raised()) {
|
||||
NEARBY_LOG(INFO, "Peer writer closed on '%s'", label.c_str());
|
||||
input->Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
NEARBY_LOG(INFO, "streaming terminated on '%s'", label.c_str());
|
||||
};
|
||||
}
|
||||
|
||||
std::function<void(const ByteArray&)> MakeDataMonitor(const std::string& label,
|
||||
std::string* capture,
|
||||
absl::Mutex* mutex) {
|
||||
return [label, capture, mutex](const ByteArray& input) mutable {
|
||||
std::string s = std::string(input);
|
||||
{
|
||||
absl::MutexLock lock(mutex);
|
||||
*capture += s;
|
||||
}
|
||||
NEARBY_LOG(INFO, "source='%s'; message='%s'", label.c_str(), s.c_str());
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<EncryptionContext>,
|
||||
std::shared_ptr<EncryptionContext>>
|
||||
DoDhKeyExchange(BaseEndpointChannel* channel_a,
|
||||
BaseEndpointChannel* channel_b) {
|
||||
std::shared_ptr<EncryptionContext> context_a;
|
||||
std::shared_ptr<EncryptionContext> context_b;
|
||||
EncryptionRunner crypto_a;
|
||||
EncryptionRunner crypto_b;
|
||||
ClientProxy proxy_a;
|
||||
ClientProxy proxy_b;
|
||||
CountDownLatch latch(2);
|
||||
crypto_a.StartClient(
|
||||
&proxy_a, "endpoint_id", channel_a,
|
||||
{
|
||||
.on_success_cb =
|
||||
[&latch, &context_a](
|
||||
const string& endpoint_id,
|
||||
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
||||
const string& auth_token, const ByteArray& raw_auth_token) {
|
||||
NEARBY_LOG(INFO, "client-A side key negotiation done");
|
||||
EXPECT_TRUE(ukey2->VerifyHandshake());
|
||||
auto context = ukey2->ToConnectionContext();
|
||||
EXPECT_NE(context, nullptr);
|
||||
context_a = std::move(context);
|
||||
latch.CountDown();
|
||||
},
|
||||
.on_failure_cb =
|
||||
[&latch](const string& endpoint_id, EndpointChannel* channel) {
|
||||
NEARBY_LOG(INFO, "client-A side key negotiation failed");
|
||||
latch.CountDown();
|
||||
},
|
||||
});
|
||||
crypto_b.StartServer(
|
||||
&proxy_b, "endpoint_id", channel_b,
|
||||
{
|
||||
.on_success_cb =
|
||||
[&latch, &context_b](
|
||||
const string& endpoint_id,
|
||||
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
||||
const string& auth_token, const ByteArray& raw_auth_token) {
|
||||
NEARBY_LOG(INFO, "client-B side key negotiation done");
|
||||
EXPECT_TRUE(ukey2->VerifyHandshake());
|
||||
auto context = ukey2->ToConnectionContext();
|
||||
EXPECT_NE(context, nullptr);
|
||||
context_b = std::move(context);
|
||||
latch.CountDown();
|
||||
},
|
||||
.on_failure_cb =
|
||||
[&latch](const string& endpoint_id, EndpointChannel* channel) {
|
||||
NEARBY_LOG(INFO, "client-B side key negotiation failed");
|
||||
latch.CountDown();
|
||||
},
|
||||
});
|
||||
EXPECT_TRUE(latch.Await(absl::Milliseconds(5000)).result());
|
||||
return std::make_pair(std::move(context_a), std::move(context_b));
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, ConstructorDestructorWorks) {
|
||||
Pipe pipe;
|
||||
InputStream& input_stream = pipe.GetInputStream();
|
||||
OutputStream& output_stream = pipe.GetOutputStream();
|
||||
|
||||
TestEndpointChannel test_channel(&input_stream, &output_stream);
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, ReadWrite) {
|
||||
// Direct not-encrypted IO.
|
||||
Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b.
|
||||
Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a.
|
||||
TestEndpointChannel channel_a(&pipe_b.GetInputStream(),
|
||||
&pipe_a.GetOutputStream());
|
||||
TestEndpointChannel channel_b(&pipe_a.GetInputStream(),
|
||||
&pipe_b.GetOutputStream());
|
||||
ByteArray tx_message{"data message"};
|
||||
channel_a.Write(tx_message);
|
||||
ByteArray rx_message = std::move(channel_b.Read().result());
|
||||
EXPECT_EQ(rx_message, tx_message);
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, NotEncryptedReadWriteCanBeIntercepted) {
|
||||
// Not encrypted IO; MITM scenario.
|
||||
|
||||
// Setup test communication environment.
|
||||
absl::Mutex mutex;
|
||||
std::string capture_a;
|
||||
std::string capture_b;
|
||||
Pipe client_a; // Channel "a" writes to client "a", reads from server "a".
|
||||
Pipe client_b; // Channel "b" writes to client "b", reads from server "b".
|
||||
Pipe server_a; // Data pump "a" reads from client "a", writes to server "b".
|
||||
Pipe server_b; // Data pump "b" reads from client "b", writes to server "a".
|
||||
TestEndpointChannel channel_a(&server_a.GetInputStream(),
|
||||
&client_a.GetOutputStream());
|
||||
TestEndpointChannel channel_b(&server_b.GetInputStream(),
|
||||
&client_b.GetOutputStream());
|
||||
|
||||
ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::BLE; });
|
||||
ON_CALL(channel_b, GetMedium).WillByDefault([]() { return Medium::BLE; });
|
||||
|
||||
MultiThreadExecutor executor(2);
|
||||
executor.Execute(MakeDataPump(
|
||||
"pump_a", &client_a.GetInputStream(), &server_b.GetOutputStream(),
|
||||
MakeDataMonitor("monitor_a", &capture_a, &mutex)));
|
||||
executor.Execute(MakeDataPump(
|
||||
"pump_b", &client_b.GetInputStream(), &server_a.GetOutputStream(),
|
||||
MakeDataMonitor("monitor_b", &capture_b, &mutex)));
|
||||
|
||||
EXPECT_EQ(channel_a.GetType(), "BLE");
|
||||
EXPECT_EQ(channel_b.GetType(), "BLE");
|
||||
|
||||
// Start data transfer
|
||||
ByteArray tx_message{"data message"};
|
||||
channel_a.Write(tx_message);
|
||||
ByteArray rx_message = std::move(channel_b.Read().result());
|
||||
|
||||
// Verify expectations.
|
||||
EXPECT_EQ(rx_message, tx_message);
|
||||
{
|
||||
absl::MutexLock lock(&mutex);
|
||||
std::string message{tx_message};
|
||||
EXPECT_TRUE(capture_a.find(message) != std::string::npos ||
|
||||
capture_b.find(message) != std::string::npos);
|
||||
}
|
||||
|
||||
// Shutdown test environment.
|
||||
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
||||
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) {
|
||||
// Encrypted IO; MITM scenario.
|
||||
|
||||
// Setup test communication environment.
|
||||
absl::Mutex mutex;
|
||||
std::string capture_a;
|
||||
std::string capture_b;
|
||||
Pipe client_a; // Channel "a" writes to client "a", reads from server "a".
|
||||
Pipe client_b; // Channel "b" writes to client "b", reads from server "b".
|
||||
Pipe server_a; // Data pump "a" reads from client "a", writes to server "b".
|
||||
Pipe server_b; // Data pump "b" reads from client "b", writes to server "a".
|
||||
TestEndpointChannel channel_a(&server_a.GetInputStream(),
|
||||
&client_a.GetOutputStream());
|
||||
TestEndpointChannel channel_b(&server_b.GetInputStream(),
|
||||
&client_b.GetOutputStream());
|
||||
|
||||
ON_CALL(channel_a, GetMedium).WillByDefault([]() {
|
||||
return Medium::BLUETOOTH;
|
||||
});
|
||||
ON_CALL(channel_b, GetMedium).WillByDefault([]() {
|
||||
return Medium::BLUETOOTH;
|
||||
});
|
||||
|
||||
MultiThreadExecutor executor(2);
|
||||
executor.Execute(MakeDataPump(
|
||||
"pump_a", &client_a.GetInputStream(), &server_b.GetOutputStream(),
|
||||
MakeDataMonitor("monitor_a", &capture_a, &mutex)));
|
||||
executor.Execute(MakeDataPump(
|
||||
"pump_b", &client_b.GetInputStream(), &server_a.GetOutputStream(),
|
||||
MakeDataMonitor("monitor_b", &capture_b, &mutex)));
|
||||
|
||||
// Run DH key exchange; setup encryption contexts for channels.
|
||||
auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b);
|
||||
ASSERT_NE(context_a, nullptr);
|
||||
ASSERT_NE(context_b, nullptr);
|
||||
channel_a.EnableEncryption(context_a);
|
||||
channel_b.EnableEncryption(context_b);
|
||||
|
||||
EXPECT_EQ(channel_a.GetType(), "ENCRYPTED_BLUETOOTH");
|
||||
EXPECT_EQ(channel_b.GetType(), "ENCRYPTED_BLUETOOTH");
|
||||
|
||||
// Start data transfer
|
||||
ByteArray tx_message{"data message"};
|
||||
channel_a.Write(tx_message);
|
||||
ByteArray rx_message = std::move(channel_b.Read().result());
|
||||
|
||||
// Verify expectations.
|
||||
EXPECT_EQ(rx_message, tx_message);
|
||||
{
|
||||
absl::MutexLock lock(&mutex);
|
||||
std::string message{tx_message};
|
||||
EXPECT_TRUE(capture_a.find(message) == std::string::npos &&
|
||||
capture_b.find(message) == std::string::npos);
|
||||
}
|
||||
|
||||
// Shutdown test environment.
|
||||
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
||||
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, CanBesuspendedAndResumed) {
|
||||
// Setup test communication environment.
|
||||
Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b.
|
||||
Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a.
|
||||
TestEndpointChannel channel_a(&pipe_b.GetInputStream(),
|
||||
&pipe_a.GetOutputStream());
|
||||
TestEndpointChannel channel_b(&pipe_a.GetInputStream(),
|
||||
&pipe_b.GetOutputStream());
|
||||
|
||||
ON_CALL(channel_a, GetMedium).WillByDefault([]() {
|
||||
return Medium::WIFI_LAN;
|
||||
});
|
||||
ON_CALL(channel_b, GetMedium).WillByDefault([]() {
|
||||
return Medium::WIFI_LAN;
|
||||
});
|
||||
|
||||
EXPECT_EQ(channel_a.GetType(), "WIFI_LAN");
|
||||
EXPECT_EQ(channel_b.GetType(), "WIFI_LAN");
|
||||
|
||||
// Start data transfer
|
||||
ByteArray tx_message{"data message"};
|
||||
ByteArray more_message{"more data"};
|
||||
channel_a.Write(tx_message);
|
||||
ByteArray rx_message = std::move(channel_b.Read().result());
|
||||
|
||||
// Pause and make sure reader blocks.
|
||||
MultiThreadExecutor pause_resume_executor(2);
|
||||
channel_a.Pause();
|
||||
pause_resume_executor.Execute([&channel_a, &more_message]() {
|
||||
// Write will block until channel is resumed, or closed.
|
||||
EXPECT_TRUE(channel_a.Write(more_message).Ok());
|
||||
});
|
||||
CountDownLatch latch(1);
|
||||
ByteArray read_more;
|
||||
pause_resume_executor.Execute([&channel_b, &read_more, &latch]() {
|
||||
// Read will block until channel is resumed, or closed.
|
||||
auto response = channel_b.Read();
|
||||
EXPECT_TRUE(response.ok());
|
||||
read_more = std::move(response.result());
|
||||
latch.CountDown();
|
||||
});
|
||||
absl::SleepFor(absl::Milliseconds(500));
|
||||
EXPECT_TRUE(read_more.Empty());
|
||||
|
||||
// Resume; verify that data transfer comepleted.
|
||||
channel_a.Resume();
|
||||
EXPECT_TRUE(latch.Await(absl::Milliseconds(1000)).result());
|
||||
EXPECT_EQ(read_more, more_message);
|
||||
|
||||
// Shutdown test environment.
|
||||
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
||||
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
||||
}
|
||||
|
||||
TEST(BaseEndpointChannelTest, ReadAfterInputStreamClosed) {
|
||||
auto pipe = MakeRefCountedPtr(new SamplePipe());
|
||||
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
|
||||
ScopedPtr<Ptr<OutputStream>> output_stream(
|
||||
SamplePipe::createOutputStream(pipe));
|
||||
Pipe pipe;
|
||||
InputStream& input_stream = pipe.GetInputStream();
|
||||
OutputStream& output_stream = pipe.GetOutputStream();
|
||||
|
||||
TestEndpointChannel test_channel(input_stream.get());
|
||||
TestEndpointChannel test_channel(&input_stream, &output_stream);
|
||||
|
||||
// Close the output stream before trying to read from the input.
|
||||
output_stream->close();
|
||||
output_stream.Close();
|
||||
|
||||
// Trying to read should fail gracefully with an IO error.
|
||||
ExceptionOr<ConstPtr<ByteArray>> result = test_channel.read();
|
||||
ExceptionOr<ByteArray> read_data = test_channel.Read();
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_EQ(Exception::IO, result.exception());
|
||||
ASSERT_FALSE(read_data.ok());
|
||||
ASSERT_TRUE(read_data.GetException().Raised(Exception::kIo));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+955
-1289
File diff suppressed because it is too large
Load Diff
@@ -2,432 +2,443 @@
|
||||
#define CORE_INTERNAL_BASE_PCP_HANDLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/internal/bandwidth_upgrade_manager.h"
|
||||
#include "core/internal/bwu_manager.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/encryption_runner.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/endpoint_manager.h"
|
||||
#include "core/internal/mediums/mediums.h"
|
||||
#include "core/internal/mediums/webrtc.h"
|
||||
#include "core/internal/pcp.h"
|
||||
#include "core/internal/pcp_handler.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#include "core/status.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/api/atomic_reference.h"
|
||||
#include "platform/api/count_down_latch.h"
|
||||
#include "platform/api/settable_future.h"
|
||||
#include "platform/api/system_clock.h"
|
||||
#include "platform/cancelable_alarm.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/prng.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/prng.h"
|
||||
#include "platform/public/atomic_boolean.h"
|
||||
#include "platform/public/atomic_reference.h"
|
||||
#include "platform/public/cancelable_alarm.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/future.h"
|
||||
#include "platform/public/scheduled_executor.h"
|
||||
#include "platform/public/single_thread_executor.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "securegcm/d2d_connection_context_v1.h"
|
||||
#include "securegcm/ukey2_handshake.h"
|
||||
#include "absl/container/btree_map.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace base_pcp_handler {
|
||||
// Define a class that supports move operation for pointers using std::swap.
|
||||
// It replicates std::unique_ptr<> behavior, but it does not own the pointer,
|
||||
// so it does not attempt destroy it.
|
||||
// This approach was recommended during code review, as a better alternative to
|
||||
// reuse of std::unique_ptr<> with custom no-op deleter, for the sake of
|
||||
// readability.
|
||||
template <typename T>
|
||||
class Swapper {
|
||||
public:
|
||||
Swapper(T* pointer) : pointer_(pointer) {} // NOLINT.
|
||||
Swapper(Swapper&& other) { *this = std::move(other); }
|
||||
Swapper& operator=(Swapper&& other) {
|
||||
std::swap(pointer_, other.pointer_);
|
||||
return *this;
|
||||
}
|
||||
T* operator->() const { return pointer_; }
|
||||
T& operator*() { return *pointer_; }
|
||||
operator T*() { return pointer_; } // NOLINT.
|
||||
T* get() const { return pointer_; }
|
||||
void reset() { pointer_ = nullptr; }
|
||||
|
||||
template <typename>
|
||||
class StartAdvertisingCallable;
|
||||
template <typename>
|
||||
class StopAdvertisingRunnable;
|
||||
template <typename>
|
||||
class StartDiscoveryCallable;
|
||||
template <typename>
|
||||
class StopDiscoveryRunnable;
|
||||
template <typename>
|
||||
class RequestConnectionRunnable;
|
||||
template <typename>
|
||||
class AcceptConnectionCallable;
|
||||
template <typename>
|
||||
class RejectConnectionCallable;
|
||||
template <typename>
|
||||
class ProcessEndpointDisconnectionRunnable;
|
||||
template <typename>
|
||||
class OnConnectionResponseRunnable;
|
||||
template <typename>
|
||||
class OnEncryptionSuccessRunnable;
|
||||
template <typename>
|
||||
class OnEncryptionFailureRunnable;
|
||||
private:
|
||||
T* pointer_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace base_pcp_handler
|
||||
template <typename T>
|
||||
Swapper<T> MakeSwapper(T* value) {
|
||||
return Swapper<T>(value);
|
||||
}
|
||||
|
||||
// A base implementation of the PCPHandler interface that takes care of all
|
||||
// bookkeeping and handshake protocols that are common across all PCPHandler
|
||||
// implementations -- thus, every concrete PCPHandler implementation must extend
|
||||
// Represents the WebRtc state that mediums are connectable or not.
|
||||
enum class WebRtcState {
|
||||
kUndefined = 0,
|
||||
kConnectable = 1,
|
||||
kUnconnectable = 2,
|
||||
};
|
||||
|
||||
// A base implementation of the PcpHandler interface that takes care of all
|
||||
// bookkeeping and handshake protocols that are common across all PcpHandler
|
||||
// implementations -- thus, every concrete PcpHandler implementation must extend
|
||||
// this class, so that they can focus exclusively on the medium-specific
|
||||
// operations.
|
||||
template <typename Platform>
|
||||
class BasePCPHandler
|
||||
: public PCPHandler<Platform>,
|
||||
public EndpointManager<Platform>::IncomingOfflineFrameProcessor {
|
||||
class BasePcpHandler : public PcpHandler,
|
||||
public EndpointManager::FrameProcessor {
|
||||
public:
|
||||
// TODO(tracyzhou): Add SecureRandom.
|
||||
BasePCPHandler(Ptr<EndpointManager<Platform> > endpoint_manager,
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager,
|
||||
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
|
||||
~BasePCPHandler() override;
|
||||
using FrameProcessor = EndpointManager::FrameProcessor;
|
||||
|
||||
// We have been asked by the client to start advertising. Once we successfully
|
||||
// start advertising, we'll change the ClientProxy's state.
|
||||
Status::Value startAdvertising(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
|
||||
const string& local_endpoint_name,
|
||||
const AdvertisingOptions& advertising_options,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener) override;
|
||||
void stopAdvertising(Ptr<ClientProxy<Platform> > client_proxy) override;
|
||||
// TODO(apolyudov): Add SecureRandom.
|
||||
BasePcpHandler(Mediums* mediums, EndpointManager* endpoint_manager,
|
||||
EndpointChannelManager* channel_manager,
|
||||
BwuManager* bwu_manager, Pcp pcp);
|
||||
~BasePcpHandler() override;
|
||||
BasePcpHandler(BasePcpHandler&&) = delete;
|
||||
BasePcpHandler& operator=(BasePcpHandler&&) = delete;
|
||||
|
||||
Status::Value startDiscovery(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
|
||||
const DiscoveryOptions& discovery_options,
|
||||
Ptr<DiscoveryListener> discovery_listener) override;
|
||||
void stopDiscovery(Ptr<ClientProxy<Platform> > client_proxy) override;
|
||||
// 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/listeners.h;l=78
|
||||
Status StartAdvertising(ClientProxy* client, const std::string& service_id,
|
||||
const ConnectionOptions& options,
|
||||
const ConnectionRequestInfo& info) override;
|
||||
|
||||
Status::Value requestConnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_name,
|
||||
const string& endpoint_id,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener) override;
|
||||
Status::Value acceptConnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<PayloadListener> payload_listener) override;
|
||||
Status::Value rejectConnection(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id) override;
|
||||
// Stops Advertising is active, and changes CLientProxy state,
|
||||
// otherwise does nothing.
|
||||
void StopAdvertising(ClientProxy* client) override;
|
||||
|
||||
proto::connections::Medium getBandwidthUpgradeMedium() override;
|
||||
// Starts discovery of endpoints that may be advertising.
|
||||
// Updates ClientProxy state once discovery started.
|
||||
// DiscoveryListener will get called in case of any event.
|
||||
Status StartDiscovery(ClientProxy* client, const std::string& service_id,
|
||||
const ConnectionOptions& options,
|
||||
const DiscoveryListener& listener) override;
|
||||
|
||||
// Stops Discovery if it is active, and changes CLientProxy state,
|
||||
// otherwise does nothing.
|
||||
void StopDiscovery(ClientProxy* client) override;
|
||||
|
||||
void InjectEndpoint(ClientProxy* client,
|
||||
const std::string& service_id,
|
||||
const OutOfBandConnectionMetadata& metadata) override;
|
||||
|
||||
// Requests a newly discovered remote endpoint it to form a connection.
|
||||
// Updates state on ClientProxy.
|
||||
Status RequestConnection(ClientProxy* client, const std::string& endpoint_id,
|
||||
const ConnectionRequestInfo& info,
|
||||
const ConnectionOptions& options) override;
|
||||
|
||||
// Called by either party to accept connection on their part.
|
||||
// Until both parties call it, connection will not reach a data phase.
|
||||
// Updates state in ClientProxy.
|
||||
Status AcceptConnection(ClientProxy* client, const std::string& endpoint_id,
|
||||
const PayloadListener& payload_listener) override;
|
||||
|
||||
// Called by either party to reject connection on their part.
|
||||
// If either party does call it, connection will terminate.
|
||||
// Updates state in ClientProxy.
|
||||
Status RejectConnection(ClientProxy* client,
|
||||
const std::string& endpoint_id) override;
|
||||
|
||||
// @EndpointManagerReaderThread
|
||||
void processIncomingOfflineFrame(
|
||||
ConstPtr<OfflineFrame> offline_frame, const string& from_endpoint_id,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
proto::connections::Medium current_medium) override;
|
||||
void OnIncomingFrame(OfflineFrame& frame, const std::string& endpoint_id,
|
||||
ClientProxy* client,
|
||||
proto::connections::Medium medium) override;
|
||||
|
||||
// Called when an endpoint disconnects while we're waiting for both sides to
|
||||
// approve/reject the connection.
|
||||
// @EndpointManagerThread
|
||||
void processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) override;
|
||||
void OnEndpointDisconnect(ClientProxy* client, const std::string& endpoint_id,
|
||||
CountDownLatch* barrier) override;
|
||||
|
||||
// Conforms to EncryptionRunner::ResultListener::onEncryptionSuccess().
|
||||
// @EncryptionRunnerThread
|
||||
void onEncryptionSuccessImpl(const string& endpoint_id,
|
||||
Ptr<securegcm::UKey2Handshake> ukey2_handshake,
|
||||
const string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token);
|
||||
|
||||
// EncryptionRunner::ResultListener::onEncryptionFailure().
|
||||
// @EncryptionRunnerThread
|
||||
void onEncryptionFailureImpl(const string& endpoint_id,
|
||||
Ptr<EndpointChannel> channel);
|
||||
Pcp GetPcp() const override { return pcp_; }
|
||||
Strategy GetStrategy() const override { return strategy_; }
|
||||
Medium GetBwuMedium() const { return bwu_medium_.Get(); }
|
||||
void DisconnectFromEndpointManager();
|
||||
|
||||
protected:
|
||||
// The result of a call to startAdvertisingImpl() or startDiscoveryImpl().
|
||||
class StartOperationResult {
|
||||
public:
|
||||
static Ptr<StartOperationResult> error(Status::Value status) {
|
||||
return MakePtr(new StartOperationResult(status));
|
||||
}
|
||||
|
||||
static Ptr<StartOperationResult> success(
|
||||
const std::vector<proto::connections::Medium>& mediums) {
|
||||
// Note: check here and not in the constructor, since for errors we have
|
||||
// null mediums.
|
||||
return MakePtr(new StartOperationResult(mediums));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StartAdvertisingCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StartDiscoveryCallable;
|
||||
|
||||
explicit StartOperationResult(Status::Value status)
|
||||
: status_(status), mediums_() {}
|
||||
explicit StartOperationResult(
|
||||
const std::vector<proto::connections::Medium>& mediums)
|
||||
: status_(Status::SUCCESS), mediums_(mediums) {}
|
||||
|
||||
// The status to be returned to the client.
|
||||
Status::Value status_;
|
||||
struct StartOperationResult {
|
||||
Status status;
|
||||
// If success, the mediums on which we are now advertising/discovering, for
|
||||
// analytics.
|
||||
std::vector<proto::connections::Medium> mediums_;
|
||||
std::vector<proto::connections::Medium> mediums;
|
||||
};
|
||||
|
||||
// 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() {}
|
||||
//
|
||||
// NOTE(DiscoveredEndpoint):
|
||||
// Specific protocol is expected to derive from it, as follows:
|
||||
// struct ProtocolEndpoint : public DiscoveredEndpoint {
|
||||
// ProtocolContext context;
|
||||
// };
|
||||
// Protocol then allocates instance with std::make_shared<ProtocolEndpoint>(),
|
||||
// and passes this instance to OnEndpointFound() method.
|
||||
// When calling OnEndpointLost(), protocol does not need to pass the same
|
||||
// instance (but it can if implementation desires to do so).
|
||||
// BasePcpHandler will hold on to the shared_ptr<DiscoveredEndpoint>.
|
||||
struct DiscoveredEndpoint {
|
||||
DiscoveredEndpoint(std::string endpoint_id, ByteArray endpoint_info,
|
||||
std::string service_id,
|
||||
proto::connections::Medium medium,
|
||||
WebRtcState web_rtc_state)
|
||||
: endpoint_id(std::move(endpoint_id)),
|
||||
endpoint_info(std::move(endpoint_info)),
|
||||
service_id(std::move(service_id)),
|
||||
medium(medium),
|
||||
web_rtc_state(web_rtc_state) {}
|
||||
virtual ~DiscoveredEndpoint() = default;
|
||||
|
||||
virtual string getEndpointId() = 0;
|
||||
virtual string getEndpointName() = 0;
|
||||
virtual string getServiceId() = 0;
|
||||
virtual proto::connections::Medium getMedium() = 0;
|
||||
std::string endpoint_id;
|
||||
ByteArray endpoint_info;
|
||||
std::string service_id;
|
||||
proto::connections::Medium medium;
|
||||
WebRtcState web_rtc_state;
|
||||
};
|
||||
|
||||
struct BluetoothEndpoint : public DiscoveredEndpoint {
|
||||
BluetoothEndpoint(DiscoveredEndpoint endpoint, BluetoothDevice device)
|
||||
: DiscoveredEndpoint(std::move(endpoint)),
|
||||
bluetooth_device(std::move(device)) {}
|
||||
|
||||
BluetoothDevice bluetooth_device;
|
||||
};
|
||||
|
||||
struct WifiLanEndpoint : public DiscoveredEndpoint {
|
||||
WifiLanEndpoint(DiscoveredEndpoint endpoint, WifiLanService service)
|
||||
: DiscoveredEndpoint(std::move(endpoint)),
|
||||
wifi_lan_service(std::move(service)) {}
|
||||
|
||||
WifiLanService wifi_lan_service;
|
||||
};
|
||||
|
||||
struct WebRtcEndpoint : public DiscoveredEndpoint {
|
||||
WebRtcEndpoint(DiscoveredEndpoint endpoint, mediums::PeerId peer_id)
|
||||
: DiscoveredEndpoint(std::move(endpoint)),
|
||||
peer_id(std::move(peer_id)) {}
|
||||
|
||||
mediums::PeerId peer_id;
|
||||
};
|
||||
|
||||
struct ConnectImplResult {
|
||||
proto::connections::Medium medium;
|
||||
Status::Value status;
|
||||
Ptr<EndpointChannel> endpoint_channel;
|
||||
|
||||
explicit ConnectImplResult(Ptr<EndpointChannel> endpoint_channel)
|
||||
: medium(proto::connections::Medium::UNKNOWN_MEDIUM),
|
||||
status(Status::SUCCESS),
|
||||
endpoint_channel(endpoint_channel) {}
|
||||
ConnectImplResult(proto::connections::Medium medium, Status::Value status)
|
||||
: medium(medium), status(status), endpoint_channel() {}
|
||||
proto::connections::Medium medium =
|
||||
proto::connections::Medium::UNKNOWN_MEDIUM;
|
||||
Status status = {Status::kError};
|
||||
std::unique_ptr<EndpointChannel> endpoint_channel;
|
||||
};
|
||||
|
||||
void runOnPCPHandlerThread(Ptr<Runnable> runnable);
|
||||
void RunOnPcpHandlerThread(Runnable runnable);
|
||||
|
||||
Ptr<AdvertisingOptions> getAdvertisingOptions();
|
||||
BluetoothDevice GetRemoteBluetoothDevice(
|
||||
const std::string& remote_bluetooth_mac_address);
|
||||
|
||||
// @PCPHandlerThread
|
||||
void onEndpointFound(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
Ptr<DiscoveredEndpoint> endpoint);
|
||||
ConnectionOptions GetConnectionOptions() const;
|
||||
ConnectionOptions GetDiscoveryOptions() const;
|
||||
|
||||
// @PCPHandlerThread
|
||||
void onEndpointLost(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
Ptr<DiscoveredEndpoint> endpoint);
|
||||
// @PcpHandlerThread
|
||||
void OnEndpointFound(ClientProxy* client,
|
||||
std::shared_ptr<DiscoveredEndpoint> endpoint);
|
||||
|
||||
Exception::Value onIncomingConnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& remote_device_name, Ptr<EndpointChannel> endpoint_channel,
|
||||
// @PcpHandlerThread
|
||||
void OnEndpointLost(ClientProxy* client, const DiscoveredEndpoint& endpoint);
|
||||
|
||||
Exception OnIncomingConnection(
|
||||
ClientProxy* client, const ByteArray& remote_endpoint_info,
|
||||
std::unique_ptr<EndpointChannel> endpoint_channel,
|
||||
proto::connections::Medium medium); // throws Exception::IO
|
||||
|
||||
virtual bool hasOutgoingConnections(Ptr<ClientProxy<Platform> > client_proxy);
|
||||
virtual bool hasIncomingConnections(Ptr<ClientProxy<Platform> > client_proxy);
|
||||
virtual bool HasOutgoingConnections(ClientProxy* client) const;
|
||||
virtual bool HasIncomingConnections(ClientProxy* client) const;
|
||||
|
||||
virtual bool canSendOutgoingConnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy);
|
||||
virtual bool canReceiveIncomingConnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy);
|
||||
virtual bool CanSendOutgoingConnection(ClientProxy* client) const;
|
||||
virtual bool CanReceiveIncomingConnection(ClientProxy* client) const;
|
||||
|
||||
// @PCPHandlerThread
|
||||
virtual Ptr<StartOperationResult> startAdvertisingImpl(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
|
||||
const string& local_endpoint_id, const string& local_endpoint_name,
|
||||
const AdvertisingOptions& options) = 0;
|
||||
// @PCPHandlerThread
|
||||
virtual Status::Value stopAdvertisingImpl(
|
||||
Ptr<ClientProxy<Platform> > client_proxy) = 0;
|
||||
// @PcpHandlerThread
|
||||
virtual StartOperationResult StartAdvertisingImpl(
|
||||
ClientProxy* client, const std::string& service_id,
|
||||
const std::string& local_endpoint_id,
|
||||
const ByteArray& local_endpoint_info,
|
||||
const ConnectionOptions& options) = 0;
|
||||
// @PcpHandlerThread
|
||||
virtual Status StopAdvertisingImpl(ClientProxy* client) = 0;
|
||||
|
||||
// @PCPHandlerThread
|
||||
virtual Ptr<StartOperationResult> startDiscoveryImpl(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
|
||||
const DiscoveryOptions& options) = 0;
|
||||
// @PCPHandlerThread
|
||||
virtual Status::Value stopDiscoveryImpl(
|
||||
Ptr<ClientProxy<Platform> > client_proxy) = 0;
|
||||
// @PcpHandlerThread
|
||||
virtual StartOperationResult StartDiscoveryImpl(
|
||||
ClientProxy* client, const std::string& service_id,
|
||||
const ConnectionOptions& options) = 0;
|
||||
// @PcpHandlerThread
|
||||
virtual Status StopDiscoveryImpl(ClientProxy* client) = 0;
|
||||
|
||||
// @PCPHandlerThread
|
||||
virtual ConnectImplResult connectImpl(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
Ptr<DiscoveredEndpoint> endpoint) = 0;
|
||||
// @PcpHandlerThread
|
||||
virtual Status InjectEndpointImpl(
|
||||
ClientProxy* client,
|
||||
const std::string& service_id,
|
||||
const OutOfBandConnectionMetadata& metadata) = 0;
|
||||
|
||||
// @PcpHandlerThread
|
||||
virtual ConnectImplResult ConnectImpl(ClientProxy* client,
|
||||
DiscoveredEndpoint* endpoint) = 0;
|
||||
|
||||
virtual std::vector<proto::connections::Medium>
|
||||
getConnectionMediumsByPriority() = 0;
|
||||
virtual proto::connections::Medium getDefaultUpgradeMedium() = 0;
|
||||
GetConnectionMediumsByPriority() = 0;
|
||||
virtual proto::connections::Medium GetDefaultUpgradeMedium() = 0;
|
||||
|
||||
Ptr<EndpointManager<Platform> > endpoint_manager_;
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager_;
|
||||
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager_;
|
||||
// Returns the first discovered endpoint for the given endpoint_id.
|
||||
DiscoveredEndpoint* GetDiscoveredEndpoint(const std::string& endpoint_id);
|
||||
|
||||
// Returns a vector of discovered endpoints, sorted in order of decreasing
|
||||
// preference.
|
||||
std::vector<BasePcpHandler::DiscoveredEndpoint*> GetDiscoveredEndpoints(
|
||||
const std::string& endpoint_id);
|
||||
|
||||
mediums::PeerId CreatePeerIdFromAdvertisement(const string& service_id,
|
||||
const string& endpoint_id,
|
||||
const ByteArray& endpoint_info);
|
||||
|
||||
Mediums* mediums_;
|
||||
EndpointManager* endpoint_manager_;
|
||||
EndpointChannelManager* channel_manager_;
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StartAdvertisingCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StopAdvertisingRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StartDiscoveryCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::StopDiscoveryRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::RequestConnectionRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::AcceptConnectionCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::RejectConnectionCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::OnConnectionResponseRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::ProcessEndpointDisconnectionRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::OnEncryptionSuccessRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::OnEncryptionFailureRunnable;
|
||||
|
||||
class ResultListenerFacade
|
||||
: public EncryptionRunner<Platform>::ResultListener {
|
||||
public:
|
||||
explicit ResultListenerFacade(Ptr<BasePCPHandler<Platform> > impl)
|
||||
: impl_(impl) {}
|
||||
|
||||
void onEncryptionSuccess(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::UKey2Handshake> ukey2_handshake,
|
||||
const string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token) override {
|
||||
impl_->onEncryptionSuccessImpl(endpoint_id, ukey2_handshake,
|
||||
authentication_token,
|
||||
raw_authentication_token);
|
||||
}
|
||||
|
||||
void onEncryptionFailure(const string& endpoint_id,
|
||||
Ptr<EndpointChannel> channel) override {
|
||||
impl_->onEncryptionFailureImpl(endpoint_id, channel);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BasePCPHandler<Platform> > impl_;
|
||||
};
|
||||
|
||||
class PendingConnectionInfo {
|
||||
public:
|
||||
static Ptr<PendingConnectionInfo> newIncomingPendingConnectionInfo(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& remote_endpoint_name,
|
||||
Ptr<EndpointChannel> endpoint_channel, std::int32_t nonce,
|
||||
std::int64_t start_time_millis,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener,
|
||||
const std::vector<proto::connections::Medium>& supported_mediums);
|
||||
|
||||
static Ptr<PendingConnectionInfo> newOutgoingPendingConnectionInfo(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& remote_endpoint_name,
|
||||
Ptr<EndpointChannel> endpoint_channel, std::int32_t nonce,
|
||||
std::int64_t start_time_millis,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener,
|
||||
Ptr<SettableFuture<Status::Value> > request_connection_result);
|
||||
|
||||
struct PendingConnectionInfo {
|
||||
PendingConnectionInfo() = default;
|
||||
PendingConnectionInfo(PendingConnectionInfo&& other) = default;
|
||||
PendingConnectionInfo& operator=(PendingConnectionInfo&&) = default;
|
||||
~PendingConnectionInfo();
|
||||
|
||||
void setUKey2Handshake(Ptr<securegcm::UKey2Handshake> ukey2_handshake);
|
||||
// Passes crypto context that we acquired in DH session for temporary
|
||||
// ownership here.
|
||||
void SetCryptoContext(std::unique_ptr<securegcm::UKey2Handshake> ukey2);
|
||||
|
||||
void localEndpointAcceptedConnection(const string& endpoint_id,
|
||||
Ptr<PayloadListener> payload_listener);
|
||||
// Pass Accept notification to client.
|
||||
void LocalEndpointAcceptedConnection(
|
||||
const std::string& endpoint_id,
|
||||
const PayloadListener& payload_listener);
|
||||
|
||||
void localEndpointRejectedConnection(const string& endpoint_id);
|
||||
// Pass Reject notification to client.
|
||||
void LocalEndpointRejectedConnection(const std::string& endpoint_id);
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend class BasePCPHandler;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::RequestConnectionRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::AcceptConnectionCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::RejectConnectionCallable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::OnEncryptionSuccessRunnable;
|
||||
template <typename>
|
||||
friend class base_pcp_handler::OnEncryptionFailureRunnable;
|
||||
// Client state tracker to report events to. Never changes. Always valid.
|
||||
ClientProxy* client = nullptr;
|
||||
// Peer endpoint info, or empty, if not discovered yet. May change.
|
||||
ByteArray remote_endpoint_info;
|
||||
std::int32_t nonce = 0;
|
||||
bool is_incoming = false;
|
||||
absl::Time start_time{absl::InfinitePast()};
|
||||
// Client callbacks. Always valid.
|
||||
ConnectionListener listener;
|
||||
ConnectionOptions options;
|
||||
|
||||
PendingConnectionInfo(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& remote_endpoint_name,
|
||||
Ptr<EndpointChannel> endpoint_channel, std::int32_t nonce,
|
||||
bool is_incoming, std::int64_t start_time_millis,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener,
|
||||
Ptr<SettableFuture<Status::Value> > request_connection_result,
|
||||
const std::vector<proto::connections::Medium>& supported_mediums);
|
||||
// Only set for outgoing connections. If set, we must call
|
||||
// result->Set() when connection is established, or rejected.
|
||||
Swapper<Future<Status>> result = nullptr;
|
||||
|
||||
Ptr<ClientProxy<Platform> > client_proxy_;
|
||||
const string remote_endpoint_name_;
|
||||
// Can be released prior to destructor.
|
||||
ScopedPtr<Ptr<EndpointChannel> > endpoint_channel_;
|
||||
const std::int32_t nonce_;
|
||||
const bool is_incoming_;
|
||||
const std::int64_t start_time_millis_;
|
||||
// Can be released prior to destructor.
|
||||
ScopedPtr<Ptr<ConnectionLifecycleListener> > connection_lifecycle_listener_;
|
||||
// Only (possibly) vector for incoming connections.
|
||||
std::vector<proto::connections::Medium> supported_mediums;
|
||||
|
||||
// Only set for outgoing connections. Can be released prior to destructor.
|
||||
// TODO(b/77783039): Consider creating a one-time-use-only wrapper class
|
||||
// around the Ptr<SettableFuture> that's passed in (that also implements the
|
||||
// SettableFuture interface) so we can avoid the easy-to-forget calls to
|
||||
// request_connection_result_.clear() peppered through multiple places in
|
||||
// the code.
|
||||
Ptr<SettableFuture<Status::Value> > request_connection_result_;
|
||||
// Keep track of a channel before we pass it to EndpointChannelManager.
|
||||
std::unique_ptr<EndpointChannel> channel;
|
||||
|
||||
// Only (possibly) set for incoming connections.
|
||||
const std::vector<proto::connections::Medium> supported_mediums_;
|
||||
|
||||
// If set, this is owned.
|
||||
Ptr<securegcm::UKey2Handshake> ukey2_handshake_;
|
||||
// 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;
|
||||
};
|
||||
|
||||
static Exception::Value writeConnectionRequestFrame(
|
||||
Ptr<EndpointChannel> endpoint_channel, const string& local_endpoint_id,
|
||||
const string& local_endpoint_name, std::int32_t nonce,
|
||||
// @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 ByteArray& local_endpoint_info, std::int32_t nonce,
|
||||
const std::vector<proto::connections::Medium>& supported_mediums);
|
||||
|
||||
static const std::int64_t kConnectionRequestReadTimeoutMillis;
|
||||
static const std::int64_t kRejectedConnectionCloseDelayMillis;
|
||||
static constexpr absl::Duration kConnectionRequestReadTimeout =
|
||||
absl::Seconds(2);
|
||||
static constexpr absl::Duration kRejectedConnectionCloseDelay =
|
||||
absl::Seconds(2);
|
||||
|
||||
template <typename T>
|
||||
Ptr<Future<T> > runOnPCPHandlerThread(Ptr<Callable<T> > callable);
|
||||
|
||||
// The interface deviates from the Java code to convey a better ownership
|
||||
// story. Ownership of 'connection_response_offline_frame' is transferred to
|
||||
// the callee by calling this method.
|
||||
void onConnectionResponse(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
ConstPtr<OfflineFrame> connection_response_offline_frame);
|
||||
void OnConnectionResponse(ClientProxy* client, const std::string& endpoint_id,
|
||||
const OfflineFrame& frame);
|
||||
|
||||
// Returns true if the new endpoint is preferred over the old endpoint.
|
||||
bool isPreferred(Ptr<DiscoveredEndpoint> new_endpoint,
|
||||
Ptr<DiscoveredEndpoint> old_endpoint);
|
||||
bool IsPreferred(const BasePcpHandler::DiscoveredEndpoint& new_endpoint,
|
||||
const BasePcpHandler::DiscoveredEndpoint& old_endpoint);
|
||||
|
||||
bool shouldEnforceTopologyConstraints();
|
||||
bool autoUpgradeBandwidth();
|
||||
// Returns true, if connection party should respect the specified topology.
|
||||
bool ShouldEnforceTopologyConstraints() 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(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id, std::int32_t incoming_nonce,
|
||||
Ptr<EndpointChannel> endpoint_channel);
|
||||
// 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(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<PendingConnectionInfo> connection_info);
|
||||
// not) have called ClientProxy::OnConnectionInitiated. Therefore, we'll
|
||||
// call both preInit and preResult failures.
|
||||
void ProcessTieBreakLoss(ClientProxy* client, const std::string& endpoint_id,
|
||||
PendingConnectionInfo* info);
|
||||
|
||||
// Called when an incoming connection has been accepted by both sides.
|
||||
//
|
||||
// @param client_proxy The client
|
||||
// @param endpoint_id The id of the remote device
|
||||
// @param supported_mediums The mediums supported by the remote device. Empty
|
||||
// @param supported_mediums The mediums supported by the remote device.
|
||||
// Empty
|
||||
// for outgoing connections and older devices that don't report their
|
||||
// supported mediums.
|
||||
void initiateBandwidthUpgrade(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
void InitiateBandwidthUpgrade(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
const std::vector<proto::connections::Medium>& supported_mediums);
|
||||
|
||||
// Returns the optimal medium supported by both devices.
|
||||
proto::connections::Medium chooseBestUpgradeMedium(
|
||||
const std::vector<proto::connections::Medium>& their_supported_mediums);
|
||||
proto::connections::Medium ChooseBestUpgradeMedium(
|
||||
const std::vector<proto::connections::Medium>& supported_mediums);
|
||||
|
||||
// This method should assume ownership of endpoint_id.
|
||||
void processPreConnectionInitiationFailure(
|
||||
Ptr<ClientProxy<Platform> > client_proxy,
|
||||
proto::connections::Medium medium, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel, bool is_incoming,
|
||||
std::int64_t start_time_millis, Status::Value status,
|
||||
Ptr<SettableFuture<Status::Value> > request_connection_result);
|
||||
void processPreConnectionResultFailure(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id);
|
||||
Ptr<DiscoveredEndpoint> getDiscoveredEndpoint(const string& endpoint_id);
|
||||
// Returns true if the bluetooth endpoint based on remote bluetooth mac
|
||||
// address is created and appended into discovered_endpoints_ with key
|
||||
// endpoint_id.
|
||||
bool AppendRemoteBluetoothMacAddressEndpoint(
|
||||
const std::string& endpoint_id,
|
||||
const std::string& remote_bluetooth_mac_address);
|
||||
|
||||
// Returns true if the webrtc endpoint is created and appended into
|
||||
// discovered_endpoints_ with key endpoint_id.
|
||||
bool AppendWebRTCEndpoint(const std::string& endpoint_id);
|
||||
|
||||
void ProcessPreConnectionInitiationFailure(const std::string& endpoint_id,
|
||||
EndpointChannel* channel,
|
||||
Status status,
|
||||
Future<Status>* result);
|
||||
void ProcessPreConnectionResultFailure(ClientProxy* client,
|
||||
const std::string& endpoint_id);
|
||||
|
||||
// Called when either side accepts/rejects the connection, but only takes
|
||||
// effect after both have accepted or one side has rejected.
|
||||
@@ -435,73 +446,68 @@ class BasePCPHandler
|
||||
// NOTE: We also take in a 'can_close_immediately' variable. This is because
|
||||
// any writes in transit are dropped when we close. To avoid having a reject
|
||||
// write being dropped (which causes the other side to report
|
||||
// onResult(DISCONNECTED) instead of onResult(REJECTED)), we delay our close.
|
||||
// If the other side behaves properly, we shouldn't even see the delay
|
||||
// (because they will also close the connection).
|
||||
void evaluateConnectionResult(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
// onResult(DISCONNECTED) instead of onResult(REJECTED)), we delay our
|
||||
// close. If the other side behaves properly, we shouldn't even see the
|
||||
// delay (because they will also close the connection).
|
||||
void EvaluateConnectionResult(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
bool can_close_immediately);
|
||||
|
||||
ExceptionOr<ConstPtr<OfflineFrame> > readConnectionRequestFrame(
|
||||
Ptr<EndpointChannel> endpoint_channel);
|
||||
ExceptionOr<OfflineFrame> ReadConnectionRequestFrame(
|
||||
EndpointChannel* channel);
|
||||
|
||||
void waitForLatch(const string& method_name, Ptr<CountDownLatch> latch);
|
||||
Status::Value waitForResult(const string& method_name, std::int64_t client_id,
|
||||
Ptr<Future<Status::Value> > result_future);
|
||||
void WaitForLatch(const std::string& method_name, CountDownLatch* latch);
|
||||
Status WaitForResult(const std::string& method_name, std::int64_t client_id,
|
||||
Future<Status>* future);
|
||||
|
||||
ScopedPtr<Ptr<AtomicReference<proto::connections::Medium> > >
|
||||
bandwidth_upgrade_medium_;
|
||||
ScopedPtr<Ptr<typename Platform::ScheduledExecutorType> > alarm_executor_;
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType> > serial_executor_;
|
||||
ScopedPtr<Ptr<SystemClock> > system_clock_;
|
||||
Prng prng_;
|
||||
AtomicReference<Medium> bwu_medium_{Medium::UNKNOWN_MEDIUM};
|
||||
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.
|
||||
typedef std::map<string, Ptr<PendingConnectionInfo> > PendingConnectionsMap;
|
||||
PendingConnectionsMap pending_connections_;
|
||||
// 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.
|
||||
typedef std::map<string, Ptr<DiscoveredEndpoint> > DiscoveredEndpointsMap;
|
||||
DiscoveredEndpointsMap discovered_endpoints_;
|
||||
absl::btree_multimap<std::string, std::shared_ptr<DiscoveredEndpoint>>
|
||||
discovered_endpoints_;
|
||||
// A map of endpoint id -> alarm. These alarms delay closing the
|
||||
// EndpointChannel to give the other side enough time to read the rejection
|
||||
// message. It's expected that the other side will close the connection after
|
||||
// reading the message (in which case, this alarm should be cancelled as it's
|
||||
// no longer needed), but this alarm is the fallback in case that doesn't
|
||||
// happen.
|
||||
typedef std::map<string, Ptr<CancelableAlarm> >
|
||||
PendingRejectedConnectionCloseAlarmsMap;
|
||||
PendingRejectedConnectionCloseAlarmsMap
|
||||
pending_rejected_connection_close_alarms_;
|
||||
// message. It's expected that the other side will close the connection
|
||||
// after reading the message (in which case, this alarm should be cancelled
|
||||
// as it's no longer needed), but this alarm is the fallback in case that
|
||||
// doesn't happen.
|
||||
absl::flat_hash_map<std::string, CancelableAlarm> pending_alarms_;
|
||||
|
||||
// The active ClientProxy's advertising constraints. Null if the client hasn't
|
||||
// started advertising. Note: this is not cleared when the client stops
|
||||
// advertising because it might still be useful downstream of advertising (eg:
|
||||
// establishing connections, performing bandwidth upgrades, etc.)
|
||||
Ptr<AdvertisingOptions> advertising_options_;
|
||||
// The active ClientProxy's advertising constraints. Empty()
|
||||
// returns true if the client hasn't started advertising false otherwise.
|
||||
// Note: this is not cleared when the client stops advertising because it
|
||||
// might still be useful downstream of advertising (eg: establishing
|
||||
// connections, performing bandwidth upgrades, etc.)
|
||||
ConnectionOptions advertising_options_;
|
||||
// The active ClientProxy's connection lifecycle listener. Non-null while
|
||||
// advertising.
|
||||
Ptr<ConnectionLifecycleListener> advertising_connection_lifecycle_listener_;
|
||||
ConnectionListener advertising_listener_;
|
||||
|
||||
// The active ClientProxy's discovery constraints. Null if the client
|
||||
// hasn't started discovering. Note: this is not cleared when the client
|
||||
// stops discovering because it might still be useful downstream of
|
||||
// discovery (eg: connection speed, etc.)
|
||||
Ptr<DiscoveryOptions> discovery_options_;
|
||||
ConnectionOptions discovery_options_;
|
||||
|
||||
// This should have been a ScopedPtr, but we are making this a Ptr to manually
|
||||
// control the order of destruction.
|
||||
Ptr<EncryptionRunner<Platform> > encryption_runner_;
|
||||
std::shared_ptr<BasePCPHandler> self_{this, [](void*){}};
|
||||
AtomicBoolean stop_{false};
|
||||
Pcp pcp_;
|
||||
Strategy strategy_{PcpToStrategy(pcp_)};
|
||||
Prng prng_;
|
||||
EncryptionRunner encryption_runner_;
|
||||
BwuManager* bwu_manager_;
|
||||
EndpointManager::FrameProcessor::Handle handle_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/base_pcp_handler.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_BASE_PCP_HANDLER_H_
|
||||
|
||||
+70
-12
@@ -1,20 +1,21 @@
|
||||
#include "core_v2/internal/base_pcp_handler.h"
|
||||
#include "core/internal/base_pcp_handler.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "core_v2/internal/base_endpoint_channel.h"
|
||||
#include "core_v2/internal/bwu_manager.h"
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/encryption_runner.h"
|
||||
#include "core_v2/internal/offline_frames.h"
|
||||
#include "core_v2/listeners.h"
|
||||
#include "core_v2/options.h"
|
||||
#include "core_v2/params.h"
|
||||
#include "core/internal/base_endpoint_channel.h"
|
||||
#include "core/internal/bwu_manager.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/encryption_runner.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#include "core/params.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/pipe.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@@ -33,6 +34,8 @@ using ::testing::MockFunction;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrictMock;
|
||||
|
||||
constexpr std::array<char, 6> kFakeMacAddress = {'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
constexpr BooleanMediumSelector kTestCases[] = {
|
||||
BooleanMediumSelector{},
|
||||
BooleanMediumSelector{
|
||||
@@ -111,6 +114,9 @@ class MockPcpHandler : public BasePcpHandler {
|
||||
const ConnectionOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(Status, StopDiscoveryImpl, (ClientProxy * client), (override));
|
||||
MOCK_METHOD(Status, InjectEndpointImpl,
|
||||
(ClientProxy * client, const std::string& service_id,
|
||||
const OutOfBandConnectionMetadata& metadata), (override));
|
||||
MOCK_METHOD(ConnectImplResult, ConnectImpl,
|
||||
(ClientProxy * client, DiscoveredEndpoint* endpoint), (override));
|
||||
MOCK_METHOD(proto::connections::Medium, GetDefaultUpgradeMedium, (),
|
||||
@@ -563,6 +569,7 @@ TEST_P(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) {
|
||||
NEARBY_LOG(INFO, "Closing connection: id=%s", endpoint_id.c_str());
|
||||
channel_b->Close();
|
||||
pcp_handler.DisconnectFromEndpointManager();
|
||||
bwu.Shutdown();
|
||||
}
|
||||
EXPECT_EQ(destroyed_flag.load(), mediums_count);
|
||||
}
|
||||
@@ -616,6 +623,57 @@ TEST_P(BasePcpHandlerTest, MultipleMediumsProduceSingleEndpointLostEvent) {
|
||||
INSTANTIATE_TEST_SUITE_P(ParameterizedBasePcpHandlerTest, BasePcpHandlerTest,
|
||||
::testing::ValuesIn(kTestCases));
|
||||
|
||||
TEST_F(BasePcpHandlerTest, InjectEndpoint) {
|
||||
std::string service_id{"service"};
|
||||
std::string endpoint_id{"ABCD"};
|
||||
ClientProxy client;
|
||||
Mediums m;
|
||||
EndpointChannelManager ecm;
|
||||
EndpointManager em(&ecm);
|
||||
BwuManager bwu(m, em, ecm, {}, {});
|
||||
MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu);
|
||||
BooleanMediumSelector allowed{ .bluetooth = true, };
|
||||
ConnectionOptions options{
|
||||
.allowed = allowed,
|
||||
.is_out_of_band_connection = true,
|
||||
};
|
||||
EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call);
|
||||
EXPECT_CALL(pcp_handler, StartDiscoveryImpl(&client, service_id, _))
|
||||
.WillOnce(Return(MockPcpHandler::StartOperationResult{
|
||||
.status = {Status::kSuccess},
|
||||
.mediums = allowed.GetMediums(true),
|
||||
}));
|
||||
EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, options,
|
||||
discovery_listener_),
|
||||
Status{Status::kSuccess});
|
||||
EXPECT_TRUE(client.IsDiscovering());
|
||||
|
||||
EXPECT_CALL(pcp_handler, InjectEndpointImpl(&client, service_id, _))
|
||||
.WillOnce(Invoke([&pcp_handler, &endpoint_id](
|
||||
ClientProxy* client,
|
||||
const std::string& service_id,
|
||||
const OutOfBandConnectionMetadata& metadata) {
|
||||
pcp_handler.OnEndpointFound(
|
||||
client,
|
||||
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
|
||||
{
|
||||
endpoint_id,
|
||||
/*endpoint_info=*/ByteArray{"ABCD"},
|
||||
service_id,
|
||||
Medium::BLUETOOTH,
|
||||
WebRtcState::kUndefined,
|
||||
},
|
||||
MockContext{nullptr},
|
||||
}));
|
||||
return Status{Status::kSuccess};
|
||||
}));
|
||||
pcp_handler.InjectEndpoint(&client, service_id,
|
||||
OutOfBandConnectionMetadata{
|
||||
.medium = Medium::BLUETOOTH,
|
||||
.remote_bluetooth_mac_address = ByteArray(kFakeMacAddress),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
@@ -1,275 +1,257 @@
|
||||
#include "core/internal/ble_advertisement.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "core/internal/base_pcp_handler.h"
|
||||
#include "platform/base/base_input_stream.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
const std::uint32_t BLEAdvertisement::kServiceIdHashLength = 3;
|
||||
|
||||
const std::uint32_t BLEAdvertisement::kVersionAndPcpLength = 1;
|
||||
// Should be defined as EndpointManager<Platform>::kEndpointIdLength, but that
|
||||
// involves making BLEAdvertisement templatized on Platform just for
|
||||
// that one little thing, so forego it (at least for now).
|
||||
const std::uint32_t BLEAdvertisement::kEndpointIdLength = 4;
|
||||
const std::uint32_t BLEAdvertisement::kEndpointNameSizeLength = 1;
|
||||
const std::uint32_t BLEAdvertisement::kBluetoothMacAddressLength = 6;
|
||||
const std::uint32_t BLEAdvertisement::kMinAdvertisementLength =
|
||||
kVersionAndPcpLength + kServiceIdHashLength + kEndpointIdLength +
|
||||
kEndpointNameSizeLength + kBluetoothMacAddressLength;
|
||||
const std::uint32_t BLEAdvertisement::kMaxEndpointNameLength = 131;
|
||||
|
||||
const std::uint16_t BLEAdvertisement::kVersionBitmask = 0x0E0;
|
||||
const std::uint16_t BLEAdvertisement::kPCPBitmask = 0x01F;
|
||||
const std::uint16_t BLEAdvertisement::kEndpointNameLengthBitmask = 0x0FF;
|
||||
|
||||
Ptr<BLEAdvertisement> BLEAdvertisement::fromBytes(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes) {
|
||||
if (ble_advertisement_bytes.isNull()) {
|
||||
// TODO(ahlee): Logger.atDebug().log("Cannot deserialize BleAdvertisement:
|
||||
// null bytes passed in.");
|
||||
return Ptr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
if (ble_advertisement_bytes->size() < kMinAdvertisementLength) {
|
||||
// TODO(ahlee): Logger.atDebug().log("Cannot deserialize BleAdvertisement:
|
||||
// expecting min %d raw bytes, got %d", kMinAdvertisementLength,
|
||||
// ble_advertisement_bytes->size());
|
||||
return Ptr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
// Start reading the bytes.
|
||||
const char* ble_advertisement_bytes_read_ptr =
|
||||
ble_advertisement_bytes->getData();
|
||||
|
||||
// The first 3 bits are supposed to be the version.
|
||||
Version::Value version = static_cast<Version::Value>(
|
||||
(*ble_advertisement_bytes_read_ptr & kVersionBitmask) >> 5);
|
||||
if (version != Version::V1) {
|
||||
// TODO(ahlee): logger.atDebug().log("Cannot deserialize BleAdvertisement:
|
||||
// unsupported Version %d", version);
|
||||
return Ptr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
PCP::Value pcp =
|
||||
static_cast<PCP::Value>(*ble_advertisement_bytes_read_ptr & kPCPBitmask);
|
||||
ble_advertisement_bytes_read_ptr++;
|
||||
if (pcp != PCP::P2P_CLUSTER && pcp != PCP::P2P_STAR &&
|
||||
pcp != PCP::P2P_POINT_TO_POINT) {
|
||||
// TODO(ahlee): logger.atDebug().log("Cannot deserialize BleAdvertisement:
|
||||
// unsupported V1 PCP %d", pcp);
|
||||
return Ptr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(ble_advertisement_bytes_read_ptr, kServiceIdHashLength)));
|
||||
ble_advertisement_bytes_read_ptr += kServiceIdHashLength;
|
||||
|
||||
std::string endpoint_id(ble_advertisement_bytes_read_ptr, kEndpointIdLength);
|
||||
ble_advertisement_bytes_read_ptr += kEndpointIdLength;
|
||||
|
||||
std::uint32_t expected_endpoint_name_length = static_cast<std::uint32_t>(
|
||||
*ble_advertisement_bytes_read_ptr & kEndpointNameLengthBitmask);
|
||||
ble_advertisement_bytes_read_ptr++;
|
||||
|
||||
// Check that the stated endpoint_name_length is the same as what we
|
||||
// received (based off of the length of ble_advertisement_bytes).
|
||||
std::uint32_t actual_endpoint_name_length =
|
||||
computeEndpointNameLength(ble_advertisement_bytes);
|
||||
if (actual_endpoint_name_length < expected_endpoint_name_length) {
|
||||
// TODO(ahlee): Logger.atDebug().log("Cannot deserialize BleAdvertisement:
|
||||
// expected endpointName to be %d bytes, got %d bytes",
|
||||
// expected_endpoint_name_length, actual_endpoint_name_length);
|
||||
return Ptr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
std::string endpoint_name(ble_advertisement_bytes_read_ptr,
|
||||
expected_endpoint_name_length);
|
||||
ble_advertisement_bytes_read_ptr += expected_endpoint_name_length;
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_bluetooth_mac_address_bytes(
|
||||
MakeConstPtr(new ByteArray(ble_advertisement_bytes_read_ptr,
|
||||
kBluetoothMacAddressLength)));
|
||||
std::string bluetooth_mac_address;
|
||||
// If the Bluetooth MAC Address bytes are unset or invalid, leave the string
|
||||
// empty. Otherwise, convert it to the proper colon delimited format.
|
||||
if (!isBluetoothMacAddressUnset(scoped_bluetooth_mac_address_bytes.get())) {
|
||||
bluetooth_mac_address = hexBytesToColonDelimitedString(
|
||||
scoped_bluetooth_mac_address_bytes.get());
|
||||
}
|
||||
|
||||
return MakePtr(
|
||||
new BLEAdvertisement(version, pcp, scoped_service_id_hash.release(),
|
||||
endpoint_id, endpoint_name, bluetooth_mac_address));
|
||||
BleAdvertisement::BleAdvertisement(Version version, Pcp pcp,
|
||||
const ByteArray& service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const std::string& bluetooth_mac_address,
|
||||
const ByteArray& uwb_address,
|
||||
WebRtcState web_rtc_state) {
|
||||
DoInitialize(/*fast_advertisement=*/false, version, pcp, service_id_hash,
|
||||
endpoint_id, endpoint_info, bluetooth_mac_address, uwb_address,
|
||||
web_rtc_state);
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisement::toBytes(
|
||||
Version::Value version, PCP::Value pcp, ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_id, const std::string& endpoint_name,
|
||||
const std::string& bluetooth_mac_address) {
|
||||
if (version != Version::V1) {
|
||||
// TODO(ahlee): logger.atDebug().log("Cannot serialize BleAdvertisement:
|
||||
// unsupported Version %d", version);
|
||||
return ConstPtr<ByteArray>();
|
||||
BleAdvertisement::BleAdvertisement(Version version, Pcp pcp,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const ByteArray& uwb_address) {
|
||||
DoInitialize(/*fast_advertisement=*/true, version, pcp, {}, endpoint_id,
|
||||
endpoint_info, {}, uwb_address, WebRtcState::kUndefined);
|
||||
}
|
||||
|
||||
void BleAdvertisement::DoInitialize(bool fast_advertisement, Version version,
|
||||
Pcp pcp, const ByteArray& service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const std::string& bluetooth_mac_address,
|
||||
const ByteArray& uwb_address,
|
||||
WebRtcState web_rtc_state) {
|
||||
fast_advertisement_ = fast_advertisement;
|
||||
if (!fast_advertisement_) {
|
||||
if (service_id_hash.size() != kServiceIdHashLength) return;
|
||||
}
|
||||
int max_endpoint_info_length =
|
||||
fast_advertisement_ ? kMaxFastEndpointInfoLength : kMaxEndpointInfoLength;
|
||||
if (version != Version::kV1 || endpoint_id.empty() ||
|
||||
endpoint_id.length() != kEndpointIdLength ||
|
||||
endpoint_info.size() > max_endpoint_info_length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcp != PCP::P2P_CLUSTER && pcp != PCP::P2P_STAR &&
|
||||
pcp != PCP::P2P_POINT_TO_POINT) {
|
||||
// TODO(ahlee): logger.atDebug().log("Cannot serialize BleAdvertisement:
|
||||
// unsupported V1 PCP %d", pcp);
|
||||
return ConstPtr<ByteArray>();
|
||||
switch (pcp) {
|
||||
case Pcp::kP2pCluster: // Fall through
|
||||
case Pcp::kP2pStar: // Fall through
|
||||
case Pcp::kP2pPointToPoint:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpoint_name.size() > kMaxEndpointNameLength) {
|
||||
// TODO(ahlee): logger.atDebug().log("Cannot serialize BleAdvertisement:
|
||||
// expected an endpointName of at most %d bytes but got %d",
|
||||
// kMaxEndpoingNameLength, endpoint_name.size());
|
||||
return ConstPtr<ByteArray>();
|
||||
version_ = version;
|
||||
pcp_ = pcp;
|
||||
service_id_hash_ = service_id_hash;
|
||||
endpoint_id_ = endpoint_id;
|
||||
endpoint_info_ = endpoint_info;
|
||||
uwb_address_ = uwb_address;
|
||||
if (!fast_advertisement_) {
|
||||
if (!BluetoothUtils::FromString(bluetooth_mac_address).Empty()) {
|
||||
bluetooth_mac_address_ = bluetooth_mac_address;
|
||||
}
|
||||
|
||||
web_rtc_state_ = web_rtc_state;
|
||||
}
|
||||
}
|
||||
|
||||
BleAdvertisement::BleAdvertisement(bool fast_advertisement,
|
||||
const ByteArray& ble_advertisement_bytes) {
|
||||
fast_advertisement_ = fast_advertisement;
|
||||
|
||||
if (ble_advertisement_bytes.Empty()) {
|
||||
NEARBY_LOG(ERROR,
|
||||
"Cannot deserialize BleAdvertisement: null bytes passed in.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint32_t ble_advertisement_length =
|
||||
computeAdvertisementLength(endpoint_name);
|
||||
Ptr<ByteArray> ble_advertisement_bytes{
|
||||
new ByteArray{ble_advertisement_length}};
|
||||
char* ble_advertisement_bytes_write_ptr = ble_advertisement_bytes->getData();
|
||||
int min_advertisement_length = fast_advertisement_
|
||||
? kMinFastAdvertisementLength
|
||||
: kMinAdvertisementLength;
|
||||
|
||||
if (ble_advertisement_bytes.size() < min_advertisement_length) {
|
||||
NEARBY_LOG(ERROR,
|
||||
"Cannot deserialize BleAdvertisement: expecting min %d raw "
|
||||
"bytes, got %" PRIu64,
|
||||
kMinAdvertisementLength, ble_advertisement_bytes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArray advertisement_bytes{ble_advertisement_bytes};
|
||||
BaseInputStream base_input_stream{advertisement_bytes};
|
||||
// The first 1 byte is supposed to be the version and pcp.
|
||||
auto version_and_pcp_byte = static_cast<char>(base_input_stream.ReadUint8());
|
||||
// The upper 3 bits are supposed to be the version.
|
||||
version_ =
|
||||
static_cast<Version>((version_and_pcp_byte & kVersionBitmask) >> 5);
|
||||
if (version_ != Version::kV1) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BleAdvertisement: unsupported Version %d",
|
||||
version_);
|
||||
return;
|
||||
}
|
||||
// The lower 5 bits are supposed to be the Pcp.
|
||||
pcp_ = static_cast<Pcp>(version_and_pcp_byte & kPcpBitmask);
|
||||
switch (pcp_) {
|
||||
case Pcp::kP2pCluster: // Fall through
|
||||
case Pcp::kP2pStar: // Fall through
|
||||
case Pcp::kP2pPointToPoint:
|
||||
break;
|
||||
default:
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BleAdvertisement: uunsupported V1 PCP %d",
|
||||
pcp_);
|
||||
}
|
||||
|
||||
// The next 3 bytes are supposed to be the service_id_hash if not fast
|
||||
// advertisment.
|
||||
if (!fast_advertisement_)
|
||||
service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength);
|
||||
|
||||
// The next 4 bytes are supposed to be the endpoint_id.
|
||||
endpoint_id_ = std::string{base_input_stream.ReadBytes(kEndpointIdLength)};
|
||||
|
||||
// The next 1 byte is supposed to be the length of the endpoint_info.
|
||||
std::uint32_t expected_endpoint_info_length = base_input_stream.ReadUint8();
|
||||
|
||||
// The next x bytes are the endpoint info. (Max length is 131 bytes or 17
|
||||
// bytes as fast_advertisement being true).
|
||||
endpoint_info_ = base_input_stream.ReadBytes(expected_endpoint_info_length);
|
||||
const int max_endpoint_info_length =
|
||||
fast_advertisement_ ? kMaxFastEndpointInfoLength : kMaxEndpointInfoLength;
|
||||
if (endpoint_info_.Empty() ||
|
||||
endpoint_info_.size() != expected_endpoint_info_length ||
|
||||
endpoint_info_.size() > max_endpoint_info_length) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BleAdvertisement(fast advertisement=%d): "
|
||||
"expected endpointInfo to be %d bytes, got %" PRIu64,
|
||||
fast_advertisement_, expected_endpoint_info_length,
|
||||
endpoint_info_.size());
|
||||
|
||||
// Clear enpoint_id for validity.
|
||||
endpoint_id_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// The next 6 bytes are the bluetooth mac address if not fast advertisment.
|
||||
if (!fast_advertisement_) {
|
||||
auto bluetooth_mac_address_bytes =
|
||||
base_input_stream.ReadBytes(BluetoothUtils::kBluetoothMacAddressLength);
|
||||
bluetooth_mac_address_ =
|
||||
BluetoothUtils::ToString(bluetooth_mac_address_bytes);
|
||||
}
|
||||
|
||||
// The next 1 byte is supposed to be the length of the uwb_address. If the
|
||||
// next byte is not available then it should be a fast advertisement and skip
|
||||
// it for remaining bytes.
|
||||
if (base_input_stream.IsAvailable(1)) {
|
||||
std::uint32_t expected_uwb_address_length = base_input_stream.ReadUint8();
|
||||
// If the length of uwb_address is not zero, then retrieve it.
|
||||
if (expected_uwb_address_length != 0) {
|
||||
uwb_address_ = base_input_stream.ReadBytes(expected_uwb_address_length);
|
||||
if (uwb_address_.Empty() ||
|
||||
uwb_address_.size() != expected_uwb_address_length) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BleAdvertisement: "
|
||||
"expected uwbAddress size to be %d bytes, got %" PRIu64,
|
||||
expected_uwb_address_length, uwb_address_.size());
|
||||
|
||||
// Clear enpoint_id for validity.
|
||||
endpoint_id_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The next 1 byte is extra field.
|
||||
if (!fast_advertisement_) {
|
||||
if (base_input_stream.IsAvailable(kExtraFieldLength)) {
|
||||
auto extra_field = static_cast<char>(base_input_stream.ReadUint8());
|
||||
web_rtc_state_ = (extra_field & kWebRtcConnectableFlagBitmask) == 1
|
||||
? WebRtcState::kConnectable
|
||||
: WebRtcState::kUnconnectable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base_input_stream.Close();
|
||||
}
|
||||
|
||||
BleAdvertisement::operator ByteArray() const {
|
||||
if (!IsValid()) {
|
||||
return ByteArray();
|
||||
}
|
||||
|
||||
// The first 3 bits are the Version.
|
||||
char version_and_pcp_byte =
|
||||
static_cast<char>((version << 5) & kVersionBitmask);
|
||||
// The next 5 bits are the PCP.
|
||||
version_and_pcp_byte |= static_cast<char>(pcp & kPCPBitmask);
|
||||
*ble_advertisement_bytes_write_ptr = version_and_pcp_byte;
|
||||
ble_advertisement_bytes_write_ptr++;
|
||||
(static_cast<char>(version_) << 5) & kVersionBitmask;
|
||||
// The next 5 bits are the Pcp.
|
||||
version_and_pcp_byte |= static_cast<char>(pcp_) & kPcpBitmask;
|
||||
|
||||
// The next 24 bits are the service id hash.
|
||||
memcpy(ble_advertisement_bytes_write_ptr, service_id_hash->getData(),
|
||||
kServiceIdHashLength);
|
||||
ble_advertisement_bytes_write_ptr += kServiceIdHashLength;
|
||||
std::string out;
|
||||
if (fast_advertisement_) {
|
||||
// clang-format off
|
||||
out = absl::StrCat(std::string(1, version_and_pcp_byte),
|
||||
endpoint_id_,
|
||||
std::string(1, endpoint_info_.size()),
|
||||
std::string(endpoint_info_));
|
||||
// clang-format on
|
||||
} else {
|
||||
// clang-format off
|
||||
out = absl::StrCat(std::string(1, version_and_pcp_byte),
|
||||
std::string(service_id_hash_),
|
||||
endpoint_id_,
|
||||
std::string(1, endpoint_info_.size()),
|
||||
std::string(endpoint_info_));
|
||||
// clang-format on
|
||||
|
||||
// The next 32 bits are the endpoint id.
|
||||
memcpy(ble_advertisement_bytes_write_ptr, endpoint_id.data(),
|
||||
kEndpointIdLength);
|
||||
ble_advertisement_bytes_write_ptr += kEndpointIdLength;
|
||||
|
||||
// The next 8 bits are the length of the endpoint name.
|
||||
*ble_advertisement_bytes_write_ptr =
|
||||
static_cast<char>(endpoint_name.size() & kEndpointNameLengthBitmask);
|
||||
ble_advertisement_bytes_write_ptr++;
|
||||
|
||||
// The next x bits are the endpoint name. (Max length is 131 bytes).
|
||||
memcpy(ble_advertisement_bytes_write_ptr, endpoint_name.data(),
|
||||
endpoint_name.size());
|
||||
ble_advertisement_bytes_write_ptr += endpoint_name.size();
|
||||
|
||||
// The next 48 bits are the bluetooth mac address. If bluetooth_mac_address is
|
||||
// invalid or empty, we get back a null byte array.
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_bluetooth_mac_address_bytes(
|
||||
bluetoothMacAddressToHexBytes(bluetooth_mac_address));
|
||||
if (!scoped_bluetooth_mac_address_bytes.isNull()) {
|
||||
memcpy(ble_advertisement_bytes_write_ptr,
|
||||
scoped_bluetooth_mac_address_bytes->getData(),
|
||||
kBluetoothMacAddressLength);
|
||||
}
|
||||
ble_advertisement_bytes_write_ptr += kBluetoothMacAddressLength;
|
||||
|
||||
return ConstifyPtr(ble_advertisement_bytes);
|
||||
}
|
||||
|
||||
std::string BLEAdvertisement::hexBytesToColonDelimitedString(
|
||||
ConstPtr<ByteArray> hex_bytes) {
|
||||
// Convert the hex bytes to a string.
|
||||
std::string colon_delimited_string(absl::BytesToHexString(
|
||||
hex_bytes->asString()));
|
||||
absl::AsciiStrToUpper(&colon_delimited_string);
|
||||
|
||||
// Insert the colons.
|
||||
for (int i = colon_delimited_string.length() - 2; i > 0; i -= 2) {
|
||||
colon_delimited_string.insert(i, ":");
|
||||
}
|
||||
return colon_delimited_string;
|
||||
}
|
||||
|
||||
// TODO(ahlee): Rename to bluetoothMacAddressHexStringToBytes
|
||||
ConstPtr<ByteArray> BLEAdvertisement::bluetoothMacAddressToHexBytes(
|
||||
const std::string& bluetooth_mac_address) {
|
||||
std::string bt_mac_address(bluetooth_mac_address);
|
||||
|
||||
// Remove the colon delimiters.
|
||||
bt_mac_address.erase(
|
||||
std::remove(bt_mac_address.begin(), bt_mac_address.end(), ':'),
|
||||
bt_mac_address.end());
|
||||
|
||||
// If the bluetooth mac address is invalid (wrong size), return a null byte
|
||||
// array.
|
||||
if (bt_mac_address.length() != kBluetoothMacAddressLength * 2) {
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
// Convert to bytes.
|
||||
std::string bt_mac_address_bytes(absl::HexStringToBytes(bt_mac_address));
|
||||
return MakeConstPtr(
|
||||
new ByteArray(bt_mac_address_bytes.data(), bt_mac_address_bytes.size()));
|
||||
}
|
||||
|
||||
bool BLEAdvertisement::isBluetoothMacAddressUnset(
|
||||
ConstPtr<ByteArray> bluetooth_mac_address_bytes) {
|
||||
for (int i = 0; i < bluetooth_mac_address_bytes->size(); i++) {
|
||||
if (bluetooth_mac_address_bytes->getData()[i] != 0) {
|
||||
return false;
|
||||
// The next 6 bytes are the bluetooth mac address. If bluetooth_mac_address
|
||||
// is invalid or empty, we get back a empty byte array.
|
||||
auto bluetooth_mac_address_bytes{
|
||||
BluetoothUtils::FromString(bluetooth_mac_address_)};
|
||||
if (!bluetooth_mac_address_bytes.Empty()) {
|
||||
absl::StrAppend(&out, std::string(bluetooth_mac_address_bytes));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint32_t BLEAdvertisement::computeEndpointNameLength(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes) {
|
||||
return ble_advertisement_bytes->size() - kMinAdvertisementLength;
|
||||
}
|
||||
// The next bytes are UWB address field.
|
||||
if (!uwb_address_.Empty()) {
|
||||
absl::StrAppend(&out, std::string(1, uwb_address_.size()));
|
||||
absl::StrAppend(&out, std::string(uwb_address_));
|
||||
} else if (!fast_advertisement_) {
|
||||
// Write UWB address with length 0 to be able to read the next field when
|
||||
// decode.
|
||||
absl::StrAppend(&out, std::string(1, uwb_address_.size()));
|
||||
}
|
||||
|
||||
std::uint32_t BLEAdvertisement::computeAdvertisementLength(
|
||||
const std::string& endpoint_name) {
|
||||
return kMinAdvertisementLength + endpoint_name.size();
|
||||
}
|
||||
// The next 1 byte is extra field.
|
||||
if (!fast_advertisement_) {
|
||||
int web_rtc_connectable_flag =
|
||||
(web_rtc_state_ == WebRtcState::kConnectable) ? 1 : 0;
|
||||
char extra_field_byte = static_cast<char>(web_rtc_connectable_flag) &
|
||||
kWebRtcConnectableFlagBitmask;
|
||||
absl::StrAppend(&out, std::string(1, extra_field_byte));
|
||||
}
|
||||
|
||||
BLEAdvertisement::BLEAdvertisement(Version::Value version, PCP::Value pcp,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const std::string& endpoint_name,
|
||||
const std::string& bluetooth_mac_address)
|
||||
: version_(version),
|
||||
pcp_(pcp),
|
||||
service_id_hash_(service_id_hash),
|
||||
endpoint_id_(endpoint_id),
|
||||
endpoint_name_(endpoint_name),
|
||||
bluetooth_mac_address_(bluetooth_mac_address) {}
|
||||
|
||||
BLEAdvertisement::~BLEAdvertisement() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
BLEAdvertisement::Version::Value BLEAdvertisement::getVersion() const {
|
||||
return version_;
|
||||
}
|
||||
|
||||
PCP::Value BLEAdvertisement::getPCP() const { return pcp_; }
|
||||
|
||||
std::string BLEAdvertisement::getEndpointId() const { return endpoint_id_; }
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisement::getServiceIdHash() const {
|
||||
return service_id_hash_.get();
|
||||
}
|
||||
|
||||
std::string BLEAdvertisement::getEndpointName() const { return endpoint_name_; }
|
||||
|
||||
std::string BLEAdvertisement::getBluetoothMacAddress() const {
|
||||
return bluetooth_mac_address_;
|
||||
return ByteArray(std::move(out));
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,91 +1,109 @@
|
||||
#ifndef CORE_INTERNAL_BLE_ADVERTISEMENT_H_
|
||||
#define CORE_INTERNAL_BLE_ADVERTISEMENT_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/internal/base_pcp_handler.h"
|
||||
#include "core/internal/pcp.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/bluetooth_utils.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
// Represents the format of the Connections BLE Advertisement used in
|
||||
// Represents the format of the Connections Ble Advertisement used in
|
||||
// Advertising + Discovery.
|
||||
//
|
||||
// <p>[VERSION][PCP][SERVICE_ID_HASH][ENDPOINT_ID][ENDPOINT_NAME_SIZE]
|
||||
// [ENDPOINT_NAME][BLUETOOTH_MAC]
|
||||
// <p>[VERSION][PCP][SERVICE_ID_HASH][ENDPOINT_ID][ENDPOINT_INFO_SIZE]
|
||||
// [ENDPOINT_INFO][BLUETOOTH_MAC][UWB_ADDRESS_SIZE][UWB_ADDRESS][EXTRA_FIELD]
|
||||
//
|
||||
// <p>The fast version of this advertisement simply omits SERVICE_ID_HASH and
|
||||
// the Bluetooth MAC address.
|
||||
//
|
||||
// <p>See go/connections-ble-advertisement for more information.
|
||||
class BLEAdvertisement {
|
||||
class BleAdvertisement {
|
||||
public:
|
||||
// Versions of the BLEAdvertisement.
|
||||
struct Version {
|
||||
enum Value {
|
||||
V1 = 1,
|
||||
// Version is only allocated 3 bits in the BLEAdvertisement, so this
|
||||
// can never go beyond V7.
|
||||
};
|
||||
// Versions of the BleAdvertisement.
|
||||
enum class Version {
|
||||
kUndefined = 0,
|
||||
kV1 = 1,
|
||||
// Version is only allocated 3 bits in the BleAdvertisement, so this
|
||||
// can never go beyond V7.
|
||||
};
|
||||
|
||||
static Ptr<BLEAdvertisement> fromBytes(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes);
|
||||
static constexpr int kVersionAndPcpLength = 1;
|
||||
static constexpr int kVersionBitmask = 0x0E0;
|
||||
static constexpr int kPcpBitmask = 0x01F;
|
||||
static constexpr int kServiceIdHashLength = 3;
|
||||
static constexpr int kEndpointIdLength = 4;
|
||||
static constexpr int kEndpointInfoSizeLength = 1;
|
||||
static constexpr int kBluetoothMacAddressLength =
|
||||
BluetoothUtils::kBluetoothMacAddressLength;
|
||||
static constexpr int kUwbAddressSizeLength = 1;
|
||||
static constexpr int kExtraFieldLength = 1;
|
||||
static constexpr int kEndpointInfoLengthBitmask = 0x0FF;
|
||||
static constexpr int kWebRtcConnectableFlagBitmask = 0x01;
|
||||
static constexpr int kMinAdvertisementLength =
|
||||
kVersionAndPcpLength + kServiceIdHashLength + kEndpointIdLength +
|
||||
kEndpointInfoSizeLength + kBluetoothMacAddressLength;
|
||||
|
||||
static ConstPtr<ByteArray> toBytes(Version::Value version, PCP::Value pcp,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const std::string& endpoint_name,
|
||||
const std::string& bluetooth_mac_address);
|
||||
// The difference between normal and fast advertisements is that the fast one
|
||||
// omits the SERVICE_ID_HASH and Bluetooth MAC address. This is done to save
|
||||
// space.
|
||||
static constexpr int kMinFastAdvertisementLength = kMinAdvertisementLength -
|
||||
kServiceIdHashLength -
|
||||
kBluetoothMacAddressLength;
|
||||
static constexpr int kMaxEndpointInfoLength = 131;
|
||||
static constexpr int kMaxFastEndpointInfoLength = 17;
|
||||
|
||||
static const std::uint32_t kServiceIdHashLength;
|
||||
static const std::uint32_t kMinAdvertisementLength;
|
||||
// TODO(ahlee): Make sure names match for both Java and C++ implementations.
|
||||
static const std::uint32_t kMaxEndpointNameLength;
|
||||
BleAdvertisement() = default;
|
||||
BleAdvertisement(Version version, Pcp pcp, const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const ByteArray& uwb_address);
|
||||
BleAdvertisement(Version version, Pcp pcp, const ByteArray& service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const std::string& bluetooth_mac_address,
|
||||
const ByteArray& uwb_address,
|
||||
WebRtcState web_rtc_state);
|
||||
BleAdvertisement(bool fast_advertisement,
|
||||
const ByteArray& ble_advertisement_bytes);
|
||||
BleAdvertisement(const BleAdvertisement&) = default;
|
||||
BleAdvertisement& operator=(const BleAdvertisement&) = default;
|
||||
BleAdvertisement(BleAdvertisement&&) = default;
|
||||
BleAdvertisement& operator=(BleAdvertisement&&) = default;
|
||||
~BleAdvertisement() = default;
|
||||
|
||||
~BLEAdvertisement();
|
||||
explicit operator ByteArray() const;
|
||||
|
||||
Version::Value getVersion() const;
|
||||
PCP::Value getPCP() const;
|
||||
ConstPtr<ByteArray> getServiceIdHash() const;
|
||||
std::string getEndpointId() const;
|
||||
std::string getEndpointName() const;
|
||||
std::string getBluetoothMacAddress() const;
|
||||
bool IsValid() const { return !endpoint_id_.empty(); }
|
||||
bool IsFastAdvertisement() const { return fast_advertisement_; }
|
||||
Version GetVersion() const { return version_; }
|
||||
Pcp GetPcp() const { return pcp_; }
|
||||
ByteArray GetServiceIdHash() const { return service_id_hash_; }
|
||||
std::string GetEndpointId() const { return endpoint_id_; }
|
||||
ByteArray GetEndpointInfo() const { return endpoint_info_; }
|
||||
std::string GetBluetoothMacAddress() const { return bluetooth_mac_address_; }
|
||||
ByteArray GetUwbAddress() const { return uwb_address_; }
|
||||
WebRtcState GetWebRtcState() const { return web_rtc_state_; }
|
||||
|
||||
private:
|
||||
static std::string hexBytesToColonDelimitedString(
|
||||
ConstPtr<ByteArray> hex_bytes);
|
||||
// TODO(ahlee): Rename to bluetoothMacAddressHexStringToBytes
|
||||
static ConstPtr<ByteArray> bluetoothMacAddressToHexBytes(
|
||||
const std::string& bluetooth_mac_address);
|
||||
static std::uint32_t computeEndpointNameLength(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes);
|
||||
static std::uint32_t computeAdvertisementLength(
|
||||
const std::string& endpoint_name);
|
||||
static bool isBluetoothMacAddressUnset(
|
||||
ConstPtr<ByteArray> bluetooth_mac_address_bytes);
|
||||
void DoInitialize(bool fast_advertisement, Version version, Pcp pcp,
|
||||
const ByteArray& service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
const std::string& bluetooth_mac_address,
|
||||
const ByteArray& uwb_address, WebRtcState web_rtc_state);
|
||||
|
||||
static const std::uint32_t kVersionAndPcpLength;
|
||||
static const std::uint32_t kEndpointIdLength;
|
||||
static const std::uint32_t kEndpointNameSizeLength;
|
||||
static const std::uint32_t kBluetoothMacAddressLength;
|
||||
static const std::uint16_t kVersionBitmask;
|
||||
static const std::uint16_t kPCPBitmask;
|
||||
static const std::uint16_t kEndpointNameLengthBitmask;
|
||||
|
||||
BLEAdvertisement(Version::Value version, PCP::Value pcp,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_id,
|
||||
const std::string& endpoint_name,
|
||||
const std::string& bluetooth_mac_address);
|
||||
|
||||
const Version::Value version_;
|
||||
const PCP::Value pcp_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > service_id_hash_;
|
||||
const std::string endpoint_id_;
|
||||
const std::string endpoint_name_;
|
||||
const std::string bluetooth_mac_address_;
|
||||
bool fast_advertisement_ = false;
|
||||
Version version_{Version::kUndefined};
|
||||
Pcp pcp_{Pcp::kUnknown};
|
||||
ByteArray service_id_hash_;
|
||||
std::string endpoint_id_;
|
||||
ByteArray endpoint_info_;
|
||||
std::string bluetooth_mac_address_;
|
||||
// TODO(b/169550050): Define UWB address field.
|
||||
ByteArray uwb_address_;
|
||||
WebRtcState web_rtc_state_{WebRtcState::kUndefined};
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "core/internal/ble_advertisement.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "platform/port/string.h"
|
||||
#include "core/internal/base_pcp_handler.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
@@ -10,341 +8,467 @@ namespace nearby {
|
||||
namespace connections {
|
||||
namespace {
|
||||
|
||||
const BLEAdvertisement::Version::Value version = BLEAdvertisement::Version::V1;
|
||||
const PCP::Value pcp = PCP::P2P_CLUSTER;
|
||||
const char endpoint_id[] = "AB12";
|
||||
const char service_id_hash_bytes[] = {0x0A, 0x0B, 0x0C};
|
||||
const char endpoint_name[] =
|
||||
"How much wood can a woodchuck chuck if a wood chuck would chuck wood?";
|
||||
const char bluetooth_mac_address[] = "00:00:E6:88:64:13";
|
||||
constexpr BleAdvertisement::Version kVersion = BleAdvertisement::Version::kV1;
|
||||
constexpr Pcp kPcp = Pcp::kP2pCluster;
|
||||
constexpr absl::string_view kServiceIdHashBytes{"\x0a\x0b\x0c"};
|
||||
constexpr absl::string_view kEndpointId{"AB12"};
|
||||
constexpr absl::string_view kEndpointName{
|
||||
"How much wood can a woodchuck chuck if a wood chuck would chuck wood?"};
|
||||
constexpr absl::string_view kFastAdvertisementEndpointName{"Fast Advertise"};
|
||||
constexpr absl::string_view kBluetoothMacAddress{"00:00:E6:88:64:13"};
|
||||
constexpr WebRtcState kWebRtcState = WebRtcState::kConnectable;
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationWorks) {
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
// TODO(b/169550050): Implement UWBAddress.
|
||||
TEST(BleAdvertisementTest, ConstructionWorks) {
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_EQ(bluetooth_mac_address,
|
||||
scoped_ble_advertisement->getBluetoothMacAddress());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_FALSE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationWorksWithGoodPCP) {
|
||||
PCP::Value good_pcp = PCP::P2P_STAR;
|
||||
TEST(BleAdvertisementTest, ConstructionWorksForFastAdvertisement) {
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, good_pcp, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_id, endpoint_name, bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(good_pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_EQ(bluetooth_mac_address,
|
||||
scoped_ble_advertisement->getBluetoothMacAddress());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_TRUE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(fast_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(WebRtcState::kUndefined, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest,
|
||||
SerializationDeserializationWorksWithEmptyEndpointName) {
|
||||
std::string empty_endpoint_name;
|
||||
TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointInfo) {
|
||||
ByteArray empty_endpoint_info;
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
empty_endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
empty_endpoint_name, bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(empty_endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_EQ(bluetooth_mac_address,
|
||||
scoped_ble_advertisement->getBluetoothMacAddress());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_FALSE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(empty_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest,
|
||||
SerializationDeSerializationFailsWithLongEndpointName) {
|
||||
std::string long_endpoint_name(BLEAdvertisement::kMaxEndpointNameLength + 1,
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionWorksWithEmptyEndpointInfoForFastAdvertisement) {
|
||||
ByteArray empty_endpoint_info;
|
||||
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
empty_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_TRUE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(empty_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(WebRtcState::kUndefined, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionWorksWithEmojiEndpointInfo) {
|
||||
ByteArray emoji_endpoint_info{std::string("\u0001F450 \u0001F450")};
|
||||
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
emoji_endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_FALSE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(emoji_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionWorksWithEmojiEndpointInfoForFastAdvertisement) {
|
||||
ByteArray emoji_endpoint_info{std::string("\u0001F450 \u0001F450")};
|
||||
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
emoji_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_TRUE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(emoji_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(WebRtcState::kUndefined, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointInfo) {
|
||||
std::string long_endpoint_name(BleAdvertisement::kMaxEndpointInfoLength + 1,
|
||||
'x');
|
||||
ByteArray long_endpoint_info{long_endpoint_name};
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
long_endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
long_endpoint_name, bluetooth_mac_address));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.get().isNull());
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest,
|
||||
SerializationDeserializationWorksWithEmojiEndpointName) {
|
||||
std::string emoji_endpoint_name("\u0001F450 \u0001F450");
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFailsWithLongEndpointInfoForFastAdvertisement) {
|
||||
std::string long_endpoint_name(
|
||||
BleAdvertisement::kMaxFastEndpointInfoLength + 1, 'x');
|
||||
ByteArray long_endpoint_info{long_endpoint_name};
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
long_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
emoji_endpoint_name, bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(emoji_endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_EQ(bluetooth_mac_address,
|
||||
scoped_ble_advertisement->getBluetoothMacAddress());
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithBadVersion) {
|
||||
BLEAdvertisement::Version::Value bad_version =
|
||||
static_cast<BLEAdvertisement::Version::Value>(666);
|
||||
TEST(BleAdvertisementTest, ConstructionFailsWithBadVersion) {
|
||||
auto bad_version = static_cast<BleAdvertisement::Version>(666);
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{bad_version,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
bad_version, pcp, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_id, endpoint_name, bluetooth_mac_address));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.get().isNull());
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithBadPCP) {
|
||||
PCP::Value bad_pcp = static_cast<PCP::Value>(666);
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFailsWithBadVersionForFastAdvertisement) {
|
||||
auto bad_version = static_cast<BleAdvertisement::Version>(666);
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement ble_advertisement{bad_version,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, bad_pcp, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_id, endpoint_name, bluetooth_mac_address));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.get().isNull());
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationSucceedsWithEmptyBluetoothMacAddress) {
|
||||
TEST(BleAdvertisementTest, ConstructionFailsWithBadPCP) {
|
||||
auto bad_pcp = static_cast<Pcp>(666);
|
||||
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
bad_pcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionFailsWithBadPCPForFastAdvertisement) {
|
||||
auto bad_pcp = static_cast<Pcp>(666);
|
||||
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
bad_pcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionSucceedsWithEmptyBluetoothMacAddress) {
|
||||
std::string empty_bluetooth_mac_address = "";
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
empty_bluetooth_mac_address,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, empty_bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_EQ(empty_bluetooth_mac_address,
|
||||
scoped_ble_advertisement->getBluetoothMacAddress());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest,
|
||||
SerializationSucceedsWithInvalidBluetoothMacAddress) {
|
||||
TEST(BleAdvertisementTest, ConstructionSucceedsWithInvalidBluetoothMacAddress) {
|
||||
std::string bad_bluetooth_mac_address = "022:00";
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
bad_bluetooth_mac_address,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bad_bluetooth_mac_address));
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_ble_advertisement->getPCP());
|
||||
ASSERT_EQ(version, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_ble_advertisement->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(service_id_hash_bytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(endpoint_name, scoped_ble_advertisement->getEndpointName());
|
||||
ASSERT_TRUE(scoped_ble_advertisement->getBluetoothMacAddress().empty());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_TRUE(ble_advertisement.GetBluetoothMacAddress().empty());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithNullBytes) {
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(ConstPtr<ByteArray>()));
|
||||
TEST(BleAdvertisementTest, ConstructionFromBytesWorks) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement org_ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
ByteArray ble_advertisement_bytes(org_ble_advertisement);
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement.get().isNull());
|
||||
BleAdvertisement ble_advertisement{false, ble_advertisement_bytes};
|
||||
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_FALSE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithShortLength) {
|
||||
// Serialize good data into a good BLE Advertisement.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
TEST(BleAdvertisementTest, ConstructionFromBytesWorksForFastAdvertisement) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement org_ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
ByteArray ble_advertisement_bytes(org_ble_advertisement);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bluetooth_mac_address));
|
||||
BleAdvertisement ble_advertisement{true, ble_advertisement_bytes};
|
||||
|
||||
// Shorten the valid BLE Advertisement.
|
||||
ScopedPtr<ConstPtr<ByteArray> > short_ble_advertisement_bytes(MakeConstPtr(
|
||||
new ByteArray(scoped_ble_advertisement_bytes.get()->getData(),
|
||||
BLEAdvertisement::kMinAdvertisementLength - 1)));
|
||||
|
||||
// Fail to deserialize the short BLE Advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_short_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(short_ble_advertisement_bytes.get()));
|
||||
ASSERT_TRUE(scoped_short_ble_advertisement.get().isNull());
|
||||
|
||||
// Make sure deserialization succeeds with the valid BLE Advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
ASSERT_FALSE(scoped_ble_advertisement.get().isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithWrongEndpointNameLength) {
|
||||
// Serialize good data into a good BLE Advertisement.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bluetooth_mac_address));
|
||||
|
||||
// Corrupt the EndpointNameLength bits.
|
||||
std::string corrupt_ble_advertisement_bytes(
|
||||
scoped_ble_advertisement_bytes->getData(),
|
||||
scoped_ble_advertisement_bytes->size());
|
||||
corrupt_ble_advertisement_bytes[8] ^= 0x0FF;
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_corrupt_ble_advertisement_bytes(
|
||||
MakeConstPtr(new ByteArray(corrupt_ble_advertisement_bytes.data(),
|
||||
corrupt_ble_advertisement_bytes.size())));
|
||||
|
||||
// And deserialize the corrupt BLE Advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(
|
||||
scoped_corrupt_ble_advertisement_bytes.get()));
|
||||
ASSERT_TRUE(scoped_ble_advertisement.isNull());
|
||||
EXPECT_TRUE(ble_advertisement.IsValid());
|
||||
EXPECT_TRUE(ble_advertisement.IsFastAdvertisement());
|
||||
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(fast_endpoint_info, ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(WebRtcState::kUndefined, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
// Bytes at the end should be ignored so that they can be used as reserve bytes
|
||||
// in the future.
|
||||
TEST(BLEAdvertisementTest, DeserializationPassesWithLongLength) {
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
ByteArray ble_advertisement_bytes(ble_advertisement);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bluetooth_mac_address));
|
||||
// Add bytes to the end of the valid Ble advertisement.
|
||||
ByteArray long_ble_advertisement_bytes(
|
||||
BleAdvertisement::kMinAdvertisementLength + 1000);
|
||||
ASSERT_LE(ble_advertisement_bytes.size(),
|
||||
long_ble_advertisement_bytes.size());
|
||||
memcpy(long_ble_advertisement_bytes.data(), ble_advertisement_bytes.data(),
|
||||
ble_advertisement_bytes.size());
|
||||
|
||||
// Add bytes to the end of the valid BLE advertisement.
|
||||
auto new_array =
|
||||
new ByteArray(BLEAdvertisement::kMinAdvertisementLength + 1000);
|
||||
ASSERT_LE(scoped_ble_advertisement_bytes->size(), new_array->size());
|
||||
memcpy(new_array->getData(),
|
||||
scoped_ble_advertisement_bytes->getData(),
|
||||
scoped_ble_advertisement_bytes->size());
|
||||
ScopedPtr<ConstPtr<ByteArray> > long_ble_advertisement_bytes(MakeConstPtr(
|
||||
new_array));
|
||||
BleAdvertisement long_ble_advertisement{false, long_ble_advertisement_bytes};
|
||||
|
||||
// Deserialize the long BLE advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_long_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(long_ble_advertisement_bytes.get()));
|
||||
ASSERT_FALSE(scoped_long_ble_advertisement.get().isNull());
|
||||
|
||||
// Make sure deserialization succeeds with the valid BLE Advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
ASSERT_FALSE(scoped_ble_advertisement.get().isNull());
|
||||
EXPECT_TRUE(long_ble_advertisement.IsValid());
|
||||
EXPECT_EQ(kVersion, long_ble_advertisement.GetVersion());
|
||||
EXPECT_EQ(kPcp, long_ble_advertisement.GetPcp());
|
||||
EXPECT_EQ(service_id_hash, long_ble_advertisement.GetServiceIdHash());
|
||||
EXPECT_EQ(kEndpointId, long_ble_advertisement.GetEndpointId());
|
||||
EXPECT_EQ(endpoint_info, long_ble_advertisement.GetEndpointInfo());
|
||||
EXPECT_EQ(kBluetoothMacAddress,
|
||||
long_ble_advertisement.GetBluetoothMacAddress());
|
||||
EXPECT_EQ(kWebRtcState, ble_advertisement.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationWorksWithLongEndpointName) {
|
||||
// Serialize good data into a good BLE Advertisement.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
TEST(BleAdvertisementTest, ConstructionFromNullBytesFails) {
|
||||
BleAdvertisement ble_advertisement{false, ByteArray{}};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
version, pcp, ConstifyPtr(scoped_service_id_hash.get()), endpoint_id,
|
||||
endpoint_name, bluetooth_mac_address));
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
// Corrupt the EndpointNameLength bits and increase it past the accepted max
|
||||
// length.
|
||||
std::string corrupt_ble_advertisement_bytes(
|
||||
scoped_ble_advertisement_bytes->getData(),
|
||||
scoped_ble_advertisement_bytes->size());
|
||||
corrupt_ble_advertisement_bytes[8] ^=
|
||||
BLEAdvertisement::kMaxEndpointNameLength + 10;
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_corrupt_ble_advertisement_bytes(
|
||||
MakeConstPtr(new ByteArray(corrupt_ble_advertisement_bytes.data(),
|
||||
corrupt_ble_advertisement_bytes.size())));
|
||||
// Increase the size of the advertisement so that there's enough data for the
|
||||
// now-longer endpoint name.
|
||||
auto new_array =
|
||||
new ByteArray(BLEAdvertisement::kMinAdvertisementLength + 1000);
|
||||
ASSERT_LE(scoped_ble_advertisement_bytes->size(), new_array->size());
|
||||
memcpy(new_array->getData(),
|
||||
scoped_ble_advertisement_bytes->getData(),
|
||||
scoped_ble_advertisement_bytes->size());
|
||||
ScopedPtr<ConstPtr<ByteArray> > long_ble_advertisement_bytes(MakeConstPtr(
|
||||
new_array));
|
||||
TEST(BleAdvertisementTest, ConstructionFromNullBytesFailsForFastAdvertisement) {
|
||||
BleAdvertisement ble_advertisement{true, ByteArray{}};
|
||||
|
||||
// And deserialize the changed BLE Advertisement.
|
||||
ScopedPtr<Ptr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(long_ble_advertisement_bytes.get()));
|
||||
ASSERT_FALSE(scoped_ble_advertisement.isNull());
|
||||
EXPECT_FALSE(ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest, ConstructionFromShortLengthBytesFails) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
ByteArray ble_advertisement_bytes(ble_advertisement);
|
||||
|
||||
// Shorten the valid Ble Advertisement.
|
||||
ByteArray short_ble_advertisement_bytes{
|
||||
ble_advertisement_bytes.data(),
|
||||
BleAdvertisement::kMinAdvertisementLength - 1};
|
||||
|
||||
BleAdvertisement short_ble_advertisement{false,
|
||||
short_ble_advertisement_bytes};
|
||||
|
||||
EXPECT_FALSE(short_ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFromShortLengthBytesFailsForFastAdvertisement) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
ByteArray ble_advertisement_bytes(ble_advertisement);
|
||||
|
||||
// Shorten the valid Ble Advertisement.
|
||||
ByteArray short_ble_advertisement_bytes{
|
||||
ble_advertisement_bytes.data(),
|
||||
BleAdvertisement::kMinAdvertisementLength - 1};
|
||||
|
||||
BleAdvertisement short_ble_advertisement{true, short_ble_advertisement_bytes};
|
||||
|
||||
EXPECT_FALSE(short_ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFromByesWithWrongEndpointInfoLengthFails) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray service_id_hash{std::string(kServiceIdHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
service_id_hash,
|
||||
std::string(kEndpointId),
|
||||
endpoint_info,
|
||||
std::string(kBluetoothMacAddress),
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
ByteArray ble_advertisement_bytes(ble_advertisement);
|
||||
|
||||
// Corrupt the EndpointNameLength bits.
|
||||
std::string corrupt_ble_advertisement_string(ble_advertisement_bytes);
|
||||
corrupt_ble_advertisement_string[8] ^= 0x0FF;
|
||||
ByteArray corrupt_ble_advertisement_bytes(corrupt_ble_advertisement_string);
|
||||
|
||||
BleAdvertisement corrupt_ble_advertisement{false,
|
||||
corrupt_ble_advertisement_bytes};
|
||||
|
||||
EXPECT_FALSE(corrupt_ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
TEST(BleAdvertisementTest,
|
||||
ConstructionFromByesWithWrongEndpointInfoLengthFailsForFastAdvertisement) {
|
||||
// Serialize good data into a good Ble Advertisement.
|
||||
ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)};
|
||||
BleAdvertisement ble_advertisement{kVersion,
|
||||
kPcp,
|
||||
std::string(kEndpointId),
|
||||
fast_endpoint_info,
|
||||
ByteArray{}};
|
||||
ByteArray ble_advertisement_bytes = ByteArray(ble_advertisement);
|
||||
|
||||
// Corrupt the EndpointInfoLength bits.
|
||||
std::string corrupt_ble_advertisement_string(ble_advertisement_bytes);
|
||||
corrupt_ble_advertisement_string[5] ^= 0x0FF;
|
||||
ByteArray corrupt_ble_advertisement_bytes(corrupt_ble_advertisement_string);
|
||||
|
||||
BleAdvertisement corrupt_ble_advertisement{true,
|
||||
corrupt_ble_advertisement_bytes};
|
||||
|
||||
EXPECT_FALSE(corrupt_ble_advertisement.IsValid());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_BLE_COMPAT_H_
|
||||
#define CORE_INTERNAL_BLE_COMPAT_H_
|
||||
|
||||
#ifndef BLE_V2_IMPLEMENTED
|
||||
// Flip to true when BLE_V2 is fully implemented and ready to be tested.
|
||||
#define BLE_V2_IMPLEMENTED 0
|
||||
#endif
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
|
||||
#include "core/internal/mediums/ble_peripheral.h"
|
||||
#include "core/internal/mediums/discovered_peripheral_callback.h"
|
||||
#define BLE_PERIPHERAL location::nearby::connections::mediums::BLEPeripheral
|
||||
#define DISCOVERED_PERIPHERAL_CALLBACK \
|
||||
location::nearby::connections::mediums::DiscoveredPeripheralCallback
|
||||
|
||||
#else
|
||||
|
||||
#include "platform/api/ble.h"
|
||||
#define BLE_PERIPHERAL location::nearby::BLEPeripheral
|
||||
#define DISCOVERED_PERIPHERAL_CALLBACK \
|
||||
BLE<Platform>::DiscoveredPeripheralCallback
|
||||
|
||||
#endif // BLE_V2_IMPLEMENTED
|
||||
|
||||
#endif // CORE_INTERNAL_BLE_COMPAT_H_
|
||||
@@ -2,40 +2,41 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "platform/public/ble.h"
|
||||
#include "platform/public/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
Ptr<BLEEndpointChannel> BLEEndpointChannel::createOutgoing(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BLESocket> ble_socket) {
|
||||
return MakePtr(new BLEEndpointChannel(channel_name, ble_socket));
|
||||
namespace {
|
||||
|
||||
OutputStream* GetOutputStreamOrNull(BleSocket& socket) {
|
||||
if (socket.GetRemotePeripheral().IsValid()) return &socket.GetOutputStream();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<BLEEndpointChannel> BLEEndpointChannel::createIncoming(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BLESocket> ble_socket) {
|
||||
return MakePtr(new BLEEndpointChannel(channel_name, ble_socket));
|
||||
InputStream* GetInputStreamOrNull(BleSocket& socket) {
|
||||
if (socket.GetRemotePeripheral().IsValid()) return &socket.GetInputStream();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLEEndpointChannel::BLEEndpointChannel(const string& channel_name,
|
||||
Ptr<BLESocket> ble_socket)
|
||||
: BaseEndpointChannel(channel_name, ble_socket->getInputStream(),
|
||||
ble_socket->getOutputStream()),
|
||||
ble_socket_(ble_socket) {}
|
||||
} // namespace
|
||||
|
||||
BLEEndpointChannel::~BLEEndpointChannel() {}
|
||||
BleEndpointChannel::BleEndpointChannel(const std::string& channel_name,
|
||||
BleSocket socket)
|
||||
: BaseEndpointChannel(channel_name, GetInputStreamOrNull(socket),
|
||||
GetOutputStreamOrNull(socket)),
|
||||
ble_socket_(std::move(socket)) {}
|
||||
|
||||
proto::connections::Medium BLEEndpointChannel::getMedium() {
|
||||
proto::connections::Medium BleEndpointChannel::GetMedium() const {
|
||||
return proto::connections::Medium::BLE;
|
||||
}
|
||||
|
||||
void BLEEndpointChannel::closeImpl() {
|
||||
Exception::Value exception = ble_socket_->close();
|
||||
if (exception != Exception::NONE) {
|
||||
if (exception == Exception::IO) {
|
||||
// TODO(ahlee): Add logging.
|
||||
}
|
||||
void BleEndpointChannel::CloseImpl() {
|
||||
auto status = ble_socket_.Close();
|
||||
if (!status.Ok()) {
|
||||
NEARBY_LOG(INFO, "Failed to close Ble socket: exception=%d", status.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,39 +2,24 @@
|
||||
#define CORE_INTERNAL_BLE_ENDPOINT_CHANNEL_H_
|
||||
|
||||
#include "core/internal/base_endpoint_channel.h"
|
||||
#include "core/internal/medium_manager.h"
|
||||
#include "platform/api/ble.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/public/ble.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
class BLEEndpointChannel : public BaseEndpointChannel {
|
||||
class BleEndpointChannel final : public BaseEndpointChannel {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
// Creates both outgoing and incoming Ble channels.
|
||||
BleEndpointChannel(const std::string& channel_name, BleSocket socket);
|
||||
|
||||
static Ptr<BLEEndpointChannel> createOutgoing(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BLESocket> ble_socket);
|
||||
static Ptr<BLEEndpointChannel> createIncoming(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BLESocket> ble_socket);
|
||||
|
||||
~BLEEndpointChannel() override;
|
||||
|
||||
proto::connections::Medium getMedium() override;
|
||||
|
||||
protected:
|
||||
void closeImpl() override;
|
||||
proto::connections::Medium GetMedium() const override;
|
||||
|
||||
private:
|
||||
BLEEndpointChannel(const string& channel_name, Ptr<BLESocket> ble_socket);
|
||||
void CloseImpl() override;
|
||||
|
||||
ScopedPtr<Ptr<BLESocket> > ble_socket_;
|
||||
BleSocket ble_socket_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,290 +1,202 @@
|
||||
#include "core/internal/bluetooth_device_name.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "platform/base64_utils.h"
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "platform/base/base_input_stream.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
const std::uint32_t BluetoothDeviceName::kServiceIdHashLength = 3;
|
||||
|
||||
const std::uint32_t BluetoothDeviceName::kMaxBluetoothDeviceNameLength = 147;
|
||||
// Should be defined as ClientProxy<Platform>::kEndpointIdLength, but that
|
||||
// involves making BluetoothDeviceName templatized on Platform just for
|
||||
// that one little thing, so forego it (at least for now).
|
||||
const std::uint32_t BluetoothDeviceName::kEndpointIdLength = 4;
|
||||
const std::uint32_t BluetoothDeviceName::kReservedLength = 7;
|
||||
const std::uint32_t BluetoothDeviceName::kMaxEndpointNameLength = 131;
|
||||
const std::uint32_t BluetoothDeviceName::kMinBluetoothDeviceNameLength =
|
||||
kMaxBluetoothDeviceNameLength - kMaxEndpointNameLength;
|
||||
|
||||
const std::uint16_t BluetoothDeviceName::kVersionBitmask = 0x0E0;
|
||||
const std::uint16_t BluetoothDeviceName::kPCPBitmask = 0x01F;
|
||||
const std::uint16_t BluetoothDeviceName::kEndpointNameLengthBitmask = 0x0FF;
|
||||
|
||||
Ptr<BluetoothDeviceName> BluetoothDeviceName::fromString(
|
||||
const std::string& bluetooth_device_name_string) {
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_bluetooth_device_name_bytes(
|
||||
Base64Utils::decode(bluetooth_device_name_string));
|
||||
if (scoped_bluetooth_device_name_bytes.isNull()) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: failed Base64 decoding of %s",
|
||||
// bluetoothDeviceNameString);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
BluetoothDeviceName::BluetoothDeviceName(Version version, Pcp pcp,
|
||||
absl::string_view endpoint_id,
|
||||
const ByteArray& service_id_hash,
|
||||
const ByteArray& endpoint_info,
|
||||
const ByteArray& uwb_address,
|
||||
WebRtcState web_rtc_state) {
|
||||
if (version != Version::kV1 || endpoint_id.empty() ||
|
||||
endpoint_id.length() != kEndpointIdLength ||
|
||||
service_id_hash.size() != kServiceIdHashLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scoped_bluetooth_device_name_bytes->size() >
|
||||
kMaxBluetoothDeviceNameLength) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: expecting max %d raw bytes, got %d",
|
||||
// MAX_BLUETOOTH_DEVICE_NAME_LENGTH, bluetoothDeviceNameBytes.length);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
}
|
||||
|
||||
if (scoped_bluetooth_device_name_bytes->size() <
|
||||
kMinBluetoothDeviceNameLength) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: expecting min %d raw bytes, got %d",
|
||||
// MIN_BLUETOOTH_DEVICE_NAME_LENGTH, bluetoothDeviceNameBytes.length);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
}
|
||||
|
||||
// The first 3 bits are supposed to be the version.
|
||||
Version::Value version = static_cast<Version::Value>(
|
||||
(scoped_bluetooth_device_name_bytes->getData()[0] & kVersionBitmask) >>
|
||||
5);
|
||||
|
||||
switch (version) {
|
||||
case Version::V1:
|
||||
return createV1BluetoothDeviceName(
|
||||
ConstifyPtr(scoped_bluetooth_device_name_bytes.get()));
|
||||
|
||||
default:
|
||||
// TODO(reznor): [ANALYTICIZE] This either represents corruption over the
|
||||
// air, or older versions of GmsCore intermingling with newer ones.
|
||||
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: unsupported Version %d", version);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
}
|
||||
}
|
||||
|
||||
std::string BluetoothDeviceName::asString(Version::Value version,
|
||||
PCP::Value pcp,
|
||||
const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_name) {
|
||||
std::string usable_endpoint_name(endpoint_name);
|
||||
if (endpoint_name.size() > kMaxEndpointNameLength) {
|
||||
// TODO(reznor): logger.atWarning().log("While serializing Advertisement,
|
||||
// truncating Endpoint Name %s (%d bytes) down to %d bytes", endpointName,
|
||||
// endpointNameBytes.length, MAX_ENDPOINT_NAME_LENGTH);
|
||||
usable_endpoint_name.erase(kMaxEndpointNameLength);
|
||||
}
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_endpoint_name_bytes(
|
||||
new ByteArray(usable_endpoint_name.data(), usable_endpoint_name.size()));
|
||||
|
||||
Ptr<ByteArray> bluetooth_device_name_bytes;
|
||||
switch (version) {
|
||||
case Version::V1:
|
||||
bluetooth_device_name_bytes =
|
||||
createV1Bytes(pcp, endpoint_id, service_id_hash,
|
||||
ConstifyPtr(scoped_endpoint_name_bytes.get()));
|
||||
if (bluetooth_device_name_bytes.isNull()) {
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO(reznor): logger.atDebug().log("Cannot serialize
|
||||
// BluetoothDeviceName: unsupported Version %d", version);
|
||||
return "";
|
||||
}
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_bluetooth_device_name_bytes(
|
||||
bluetooth_device_name_bytes);
|
||||
|
||||
// BluetoothDeviceName needs to be binary safe, so apply a Base64 encoding
|
||||
// over the raw bytes.
|
||||
return Base64Utils::encode(
|
||||
ConstifyPtr(scoped_bluetooth_device_name_bytes.get()));
|
||||
}
|
||||
|
||||
Ptr<BluetoothDeviceName> BluetoothDeviceName::createV1BluetoothDeviceName(
|
||||
ConstPtr<ByteArray> bluetooth_device_name_bytes) {
|
||||
const char* bluetooth_device_name_bytes_read_ptr =
|
||||
bluetooth_device_name_bytes->getData();
|
||||
|
||||
// The first 5 bits of the V1 payload are supposed to be the PCP.
|
||||
PCP::Value pcp = static_cast<PCP::Value>(
|
||||
*bluetooth_device_name_bytes_read_ptr & kPCPBitmask);
|
||||
bluetooth_device_name_bytes_read_ptr++;
|
||||
|
||||
switch (pcp) {
|
||||
case PCP::P2P_CLUSTER: // Fall through
|
||||
case PCP::P2P_STAR: // Fall through
|
||||
case PCP::P2P_POINT_TO_POINT: {
|
||||
// The next 32 bits are supposed to be the endpoint_id.
|
||||
std::string endpoint_id(bluetooth_device_name_bytes_read_ptr,
|
||||
kEndpointIdLength);
|
||||
bluetooth_device_name_bytes_read_ptr += kEndpointIdLength;
|
||||
case Pcp::kP2pCluster: // Fall through
|
||||
case Pcp::kP2pStar: // Fall through
|
||||
case Pcp::kP2pPointToPoint:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// The next 24 bits are supposed to be the scoped_service_id_hash.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(bluetooth_device_name_bytes_read_ptr,
|
||||
kServiceIdHashLength)));
|
||||
bluetooth_device_name_bytes_read_ptr += kServiceIdHashLength;
|
||||
version_ = version;
|
||||
pcp_ = pcp;
|
||||
endpoint_id_ = std::string(endpoint_id);
|
||||
service_id_hash_ = service_id_hash;
|
||||
endpoint_info_ = endpoint_info;
|
||||
uwb_address_ = uwb_address;
|
||||
web_rtc_state_ = web_rtc_state;
|
||||
}
|
||||
|
||||
// The next 56 bits are supposed to be reserved, and can be left
|
||||
// untouched.
|
||||
bluetooth_device_name_bytes_read_ptr += kReservedLength;
|
||||
BluetoothDeviceName::BluetoothDeviceName(
|
||||
absl::string_view bluetooth_device_name_string) {
|
||||
ByteArray bluetooth_device_name_bytes =
|
||||
Base64Utils::Decode(bluetooth_device_name_string);
|
||||
|
||||
// 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>(
|
||||
*bluetooth_device_name_bytes_read_ptr & kEndpointNameLengthBitmask);
|
||||
bluetooth_device_name_bytes_read_ptr++;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 =
|
||||
computeEndpointNameLength(bluetooth_device_name_bytes);
|
||||
if (actual_endpoint_name_length != expected_endpoint_name_length) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: expected endpointName to be %d bytes, got %d
|
||||
// bytes", expectedEndpointNameLength, actualEndpointNameLength);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
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;
|
||||
}
|
||||
|
||||
BaseInputStream base_input_stream{bluetooth_device_name_bytes};
|
||||
// The first 1 byte is supposed to be the version and pcp.
|
||||
auto version_and_pcp_byte = static_cast<char>(base_input_stream.ReadUint8());
|
||||
// The upper 3 bits are supposed to be the version.
|
||||
version_ =
|
||||
static_cast<Version>((version_and_pcp_byte & kVersionBitmask) >> 5);
|
||||
if (version_ != Version::kV1) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BluetoothDeviceName: unsupported version=%d",
|
||||
version_);
|
||||
return;
|
||||
}
|
||||
// The lower 5 bits are supposed to be the Pcp.
|
||||
pcp_ = static_cast<Pcp>(version_and_pcp_byte & kPcpBitmask);
|
||||
switch (pcp_) {
|
||||
case Pcp::kP2pCluster: // Fall through
|
||||
case Pcp::kP2pStar: // Fall through
|
||||
case Pcp::kP2pPointToPoint:
|
||||
break;
|
||||
default:
|
||||
NEARBY_LOG(
|
||||
INFO, "Cannot deserialize BluetoothDeviceName: unsupported V1 PCP %d",
|
||||
pcp_);
|
||||
return;
|
||||
}
|
||||
|
||||
// The next 4 bytes are supposed to be the endpoint_id.
|
||||
endpoint_id_ = std::string{base_input_stream.ReadBytes(kEndpointIdLength)};
|
||||
|
||||
// The next 3 bytes are supposed to be the service_id_hash.
|
||||
service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength);
|
||||
|
||||
|
||||
// The next 1 byte is field containning WebRtc state.
|
||||
auto field_byte = static_cast<char>(base_input_stream.ReadUint8());
|
||||
web_rtc_state_ = (field_byte & kWebRtcConnectableFlagBitmask) == 1
|
||||
? WebRtcState::kConnectable
|
||||
: WebRtcState::kUnconnectable;
|
||||
|
||||
// The next 6 bytes are supposed to be reserved, and can be left
|
||||
// untouched.
|
||||
base_input_stream.ReadBytes(kReservedLength);
|
||||
|
||||
// The next 1 byte is supposed to be the length of the endpoint_info.
|
||||
std::uint32_t expected_endpoint_info_length = base_input_stream.ReadUint8();
|
||||
|
||||
// The rest bytes are supposed to be the endpoint_info
|
||||
endpoint_info_ = base_input_stream.ReadBytes(expected_endpoint_info_length);
|
||||
if (endpoint_info_.Empty() ||
|
||||
endpoint_info_.size() != expected_endpoint_info_length) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BluetoothDeviceName: expected "
|
||||
"endpoint info to be %d bytes, got %" PRIu64,
|
||||
expected_endpoint_info_length, endpoint_info_.size());
|
||||
|
||||
// Clear enpoint_id for validadity.
|
||||
endpoint_id_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the input stream has extra bytes, it's for UWB address. The first byte
|
||||
// is the address length. It can be 2-byte short address or 8-byte extended
|
||||
// address.
|
||||
if (base_input_stream.IsAvailable(1)) {
|
||||
// The next 1 byte is supposed to be the length of the uwb_address.
|
||||
std::uint32_t expected_uwb_address_length = base_input_stream.ReadUint8();
|
||||
// If the length of usb_address is not zero, then retrieve it.
|
||||
if (expected_uwb_address_length != 0) {
|
||||
uwb_address_ = base_input_stream.ReadBytes(expected_uwb_address_length);
|
||||
if (uwb_address_.Empty() ||
|
||||
uwb_address_.size() != expected_uwb_address_length) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BluetoothDeviceName: "
|
||||
"expected uwbAddress size to be %d bytes, got %" PRIu64,
|
||||
expected_uwb_address_length, uwb_address_.size());
|
||||
|
||||
// Clear enpoint_id for validadity.
|
||||
endpoint_id_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string endpoint_name(bluetooth_device_name_bytes_read_ptr,
|
||||
actual_endpoint_name_length);
|
||||
bluetooth_device_name_bytes_read_ptr += actual_endpoint_name_length;
|
||||
|
||||
return MakePtr(new BluetoothDeviceName(Version::V1, pcp, endpoint_id,
|
||||
scoped_service_id_hash.release(),
|
||||
endpoint_name));
|
||||
}
|
||||
default:
|
||||
// TODO(reznor): [ANALYTICIZE] This either represents corruption over the
|
||||
// air, or older versions of GmsCore intermingling with newer ones.
|
||||
|
||||
// TODO(reznor): logger.atDebug().log("Cannot deserialize
|
||||
// BluetoothDeviceName: unsupported V1 PCP %d", pcp);
|
||||
return Ptr<BluetoothDeviceName>();
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t BluetoothDeviceName::computeEndpointNameLength(
|
||||
ConstPtr<ByteArray> bluetooth_device_name_bytes) {
|
||||
return kMaxEndpointNameLength -
|
||||
(kMaxBluetoothDeviceNameLength - bluetooth_device_name_bytes->size());
|
||||
}
|
||||
|
||||
std::uint32_t BluetoothDeviceName::computeBluetoothDeviceNameLength(
|
||||
ConstPtr<ByteArray> endpoint_name_bytes) {
|
||||
return kMaxBluetoothDeviceNameLength -
|
||||
(kMaxEndpointNameLength - endpoint_name_bytes->size());
|
||||
}
|
||||
|
||||
Ptr<ByteArray> BluetoothDeviceName::createV1Bytes(
|
||||
PCP::Value pcp, const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> endpoint_name_bytes) {
|
||||
std::uint32_t bluetooth_device_name_length =
|
||||
computeBluetoothDeviceNameLength(endpoint_name_bytes);
|
||||
Ptr<ByteArray> bluetooth_device_name_bytes{
|
||||
new ByteArray{bluetooth_device_name_length}};
|
||||
|
||||
char* bluetooth_device_name_bytes_write_ptr =
|
||||
bluetooth_device_name_bytes->getData();
|
||||
|
||||
// The first 3 bits are the Version.
|
||||
char version_and_pcp_byte =
|
||||
static_cast<char>((Version::V1 << 5) & kVersionBitmask);
|
||||
// The next 5 bits are the PCP.
|
||||
version_and_pcp_byte |= static_cast<char>(pcp & kPCPBitmask);
|
||||
*bluetooth_device_name_bytes_write_ptr = version_and_pcp_byte;
|
||||
bluetooth_device_name_bytes_write_ptr++;
|
||||
|
||||
switch (pcp) {
|
||||
case PCP::P2P_CLUSTER: // Fall through
|
||||
case PCP::P2P_STAR: // Fall through
|
||||
case PCP::P2P_POINT_TO_POINT:
|
||||
// The next 32 bits are the endpoint_id.
|
||||
if (endpoint_id.size() != kEndpointIdLength) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot serialize
|
||||
// BluetoothDeviceName: V1 Endpoint ID %s (%d bytes) should be exactly
|
||||
// %d bytes", endpointId, endpointId.length(), ENDPOINT_ID_LENGTH);
|
||||
return Ptr<ByteArray>();
|
||||
}
|
||||
memcpy(bluetooth_device_name_bytes_write_ptr, endpoint_id.data(),
|
||||
kEndpointIdLength);
|
||||
bluetooth_device_name_bytes_write_ptr += kEndpointIdLength;
|
||||
|
||||
// The next 24 bits are the service_id_hash.
|
||||
if (service_id_hash->size() != kServiceIdHashLength) {
|
||||
// TODO(reznor): logger.atDebug().log("Cannot serialize
|
||||
// BluetoothDeviceName: V1 ServiceID hash (%d bytes) should be exactly
|
||||
// %d bytes", serviceIdHash.length, SERVICE_ID_HASH_LENGTH);
|
||||
return Ptr<ByteArray>();
|
||||
}
|
||||
memcpy(bluetooth_device_name_bytes_write_ptr, service_id_hash->getData(),
|
||||
kServiceIdHashLength);
|
||||
bluetooth_device_name_bytes_write_ptr += kServiceIdHashLength;
|
||||
|
||||
// The next 56 bits are reserved, and should all be zeroed out, so do
|
||||
// that and then jump over 56 bits to position things for the next write.
|
||||
memset(bluetooth_device_name_bytes_write_ptr, 0, kReservedLength);
|
||||
bluetooth_device_name_bytes_write_ptr += kReservedLength;
|
||||
|
||||
// The next 8 bits are the length of the endpoint_name.
|
||||
*bluetooth_device_name_bytes_write_ptr = static_cast<char>(
|
||||
endpoint_name_bytes->size() & kEndpointNameLengthBitmask);
|
||||
bluetooth_device_name_bytes_write_ptr++;
|
||||
|
||||
// The remaining bits are filled with the endpoint_name.
|
||||
memcpy(bluetooth_device_name_bytes_write_ptr,
|
||||
endpoint_name_bytes->getData(), endpoint_name_bytes->size());
|
||||
bluetooth_device_name_bytes_write_ptr += endpoint_name_bytes->size();
|
||||
|
||||
break;
|
||||
default:
|
||||
// TODO(reznor): logger.atDebug().log("Cannot serialize
|
||||
// BluetoothDeviceName: unsupported V1 PCP %d", pcp);
|
||||
return Ptr<ByteArray>();
|
||||
BluetoothDeviceName::operator std::string() const {
|
||||
if (!IsValid()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return bluetooth_device_name_bytes;
|
||||
}
|
||||
// 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);
|
||||
|
||||
BluetoothDeviceName::BluetoothDeviceName(Version::Value version, PCP::Value pcp,
|
||||
const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_name)
|
||||
: version_(version),
|
||||
pcp_(pcp),
|
||||
endpoint_id_(endpoint_id),
|
||||
service_id_hash_(service_id_hash),
|
||||
endpoint_name_(endpoint_name) {}
|
||||
// A byte contains WebRtcState state.
|
||||
int web_rtc_connectable_flag =
|
||||
(web_rtc_state_ == WebRtcState::kConnectable) ? 1 : 0;
|
||||
char field_byte = static_cast<char>(web_rtc_connectable_flag) &
|
||||
kWebRtcConnectableFlagBitmask;
|
||||
|
||||
BluetoothDeviceName::~BluetoothDeviceName() {
|
||||
// Nothing to do.
|
||||
}
|
||||
ByteArray reserved_bytes{kReservedLength};
|
||||
|
||||
BluetoothDeviceName::Version::Value BluetoothDeviceName::getVersion() const {
|
||||
return version_;
|
||||
}
|
||||
ByteArray usable_endpoint_info(endpoint_info_);
|
||||
if (endpoint_info_.size() > kMaxEndpointInfoLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"While serializing Advertisement, truncating Endpoint Name %s "
|
||||
"(%lu bytes) down to %d bytes",
|
||||
absl::BytesToHexString(endpoint_info_.data()).c_str(),
|
||||
endpoint_info_.size(), kMaxEndpointInfoLength);
|
||||
usable_endpoint_info.SetData(endpoint_info_.data(), kMaxEndpointInfoLength);
|
||||
}
|
||||
|
||||
PCP::Value BluetoothDeviceName::getPCP() const { return pcp_; }
|
||||
// clang-format off
|
||||
std::string out = absl::StrCat(std::string(1, version_and_pcp_byte),
|
||||
endpoint_id_,
|
||||
std::string(service_id_hash_),
|
||||
std::string(1, field_byte),
|
||||
std::string(reserved_bytes),
|
||||
std::string(1, usable_endpoint_info.size()),
|
||||
std::string(usable_endpoint_info));
|
||||
// clang-format on
|
||||
|
||||
std::string BluetoothDeviceName::getEndpointId() const { return endpoint_id_; }
|
||||
// If UWB address is available, attach it at the end.
|
||||
if (!uwb_address_.Empty()) {
|
||||
absl::StrAppend(&out, std::string(1, uwb_address_.size()));
|
||||
absl::StrAppend(&out, std::string(uwb_address_));
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BluetoothDeviceName::getServiceIdHash() const {
|
||||
return service_id_hash_.get();
|
||||
}
|
||||
|
||||
std::string BluetoothDeviceName::getEndpointName() const {
|
||||
return endpoint_name_;
|
||||
return Base64Utils::Encode(ByteArray{std::move(out)});
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/internal/base_pcp_handler.h"
|
||||
#include "core/internal/pcp.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -19,64 +19,58 @@ namespace connections {
|
||||
class BluetoothDeviceName {
|
||||
public:
|
||||
// Versions of the BluetoothDeviceName.
|
||||
struct Version {
|
||||
enum Value {
|
||||
V1 = 1,
|
||||
// Version is only allocated 3 bits in the BluetoothDeviceName, so this
|
||||
// can never go beyond V7.
|
||||
};
|
||||
enum class Version {
|
||||
kUndefined = 0,
|
||||
kV1 = 1,
|
||||
// Version is only allocated 3 bits in the BluetoothDeviceName, so this
|
||||
// can never go beyond V7.
|
||||
};
|
||||
|
||||
static Ptr<BluetoothDeviceName> fromString(
|
||||
const std::string& bluetooth_device_name_string);
|
||||
static constexpr int kServiceIdHashLength = 3;
|
||||
|
||||
static std::string asString(Version::Value version, PCP::Value pcp,
|
||||
const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_name);
|
||||
BluetoothDeviceName() = default;
|
||||
BluetoothDeviceName(Version version, Pcp pcp, absl::string_view endpoint_id,
|
||||
const ByteArray& service_id_hash,
|
||||
const ByteArray& endpoint_info,
|
||||
const ByteArray& uwb_address,
|
||||
WebRtcState web_rtc_state);
|
||||
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;
|
||||
|
||||
static const std::uint32_t kServiceIdHashLength;
|
||||
explicit operator std::string() const;
|
||||
|
||||
~BluetoothDeviceName();
|
||||
|
||||
Version::Value getVersion() const;
|
||||
PCP::Value getPCP() const;
|
||||
std::string getEndpointId() const;
|
||||
ConstPtr<ByteArray> getServiceIdHash() const;
|
||||
std::string getEndpointName() 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_; }
|
||||
ByteArray GetEndpointInfo() const { return endpoint_info_; }
|
||||
ByteArray GetUwbAddress() const { return uwb_address_; }
|
||||
WebRtcState GetWebRtcState() const { return web_rtc_state_; }
|
||||
|
||||
private:
|
||||
static Ptr<BluetoothDeviceName> createV1BluetoothDeviceName(
|
||||
ConstPtr<ByteArray> bluetooth_device_name_bytes);
|
||||
static std::uint32_t computeEndpointNameLength(
|
||||
ConstPtr<ByteArray> bluetooth_device_name_bytes);
|
||||
static std::uint32_t computeBluetoothDeviceNameLength(
|
||||
ConstPtr<ByteArray> endpoint_name_bytes);
|
||||
static Ptr<ByteArray> createV1Bytes(PCP::Value pcp,
|
||||
const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> endpoint_name_bytes);
|
||||
static constexpr int kEndpointIdLength = 4;
|
||||
static constexpr int kReservedLength = 6;
|
||||
static constexpr int kMaxEndpointInfoLength = 131;
|
||||
static constexpr int kMinBluetoothDeviceNameLength = 16;
|
||||
|
||||
static const std::uint32_t kMaxBluetoothDeviceNameLength;
|
||||
static const std::uint32_t kEndpointIdLength;
|
||||
static const std::uint32_t kReservedLength;
|
||||
static const std::uint32_t kMaxEndpointNameLength;
|
||||
static const std::uint32_t kMinBluetoothDeviceNameLength;
|
||||
static constexpr int kVersionBitmask = 0x0E0;
|
||||
static constexpr int kPcpBitmask = 0x01F;
|
||||
static constexpr int kEndpointNameLengthBitmask = 0x0FF;
|
||||
static constexpr int kWebRtcConnectableFlagBitmask = 0x01;
|
||||
|
||||
static const std::uint16_t kVersionBitmask;
|
||||
static const std::uint16_t kPCPBitmask;
|
||||
static const std::uint16_t kEndpointNameLengthBitmask;
|
||||
|
||||
BluetoothDeviceName(Version::Value version, PCP::Value pcp,
|
||||
const std::string& endpoint_id,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
const std::string& endpoint_name);
|
||||
|
||||
const Version::Value version_;
|
||||
const PCP::Value pcp_;
|
||||
const std::string endpoint_id_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > service_id_hash_;
|
||||
const std::string endpoint_name_;
|
||||
Version version_{Version::kUndefined};
|
||||
Pcp pcp_{Pcp::kUnknown};
|
||||
std::string endpoint_id_;
|
||||
ByteArray service_id_hash_;
|
||||
ByteArray endpoint_info_;
|
||||
// TODO(b/169550050): Define UWB address field.
|
||||
ByteArray uwb_address_;
|
||||
WebRtcState web_rtc_state_{WebRtcState::kUndefined};
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "core/internal/bluetooth_device_name.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "platform/base64_utils.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
@@ -11,185 +11,216 @@ namespace nearby {
|
||||
namespace connections {
|
||||
namespace {
|
||||
|
||||
const BluetoothDeviceName::Version::Value version =
|
||||
BluetoothDeviceName::Version::V1;
|
||||
const PCP::Value pcp = PCP::P2P_CLUSTER;
|
||||
const char endpoint_id[] = "AB12";
|
||||
const char service_id_hash_bytes[] = {0x0A, 0x0B, 0x0C};
|
||||
const char endpoint_name[] = "RAWK + ROWL!";
|
||||
constexpr BluetoothDeviceName::Version kVersion =
|
||||
BluetoothDeviceName::Version::kV1;
|
||||
constexpr Pcp kPcp = Pcp::kP2pCluster;
|
||||
constexpr absl::string_view kEndPointID{"AB12"};
|
||||
constexpr absl::string_view kServiceIDHashBytes{"\x0a\x0b\x0c"};
|
||||
constexpr absl::string_view kEndPointName{"RAWK + ROWL!"};
|
||||
constexpr WebRtcState kWebRtcState = WebRtcState::kConnectable;
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationDeserializationWorks) {
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
// TODO(b/169550050): Implement UWBAddress.
|
||||
TEST(BluetoothDeviceNameTest, ConstructionWorks) {
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
ScopedPtr<Ptr<BluetoothDeviceName> > scoped_bluetooth_device_name(
|
||||
BluetoothDeviceName::fromString(bluetooth_device_name_string));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_bluetooth_device_name->getPCP());
|
||||
ASSERT_EQ(version, scoped_bluetooth_device_name->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_bluetooth_device_name->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0,
|
||||
memcmp(service_id_hash_bytes,
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->getData(),
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(endpoint_name, scoped_bluetooth_device_name->getEndpointName());
|
||||
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(endpoint_info, bluetooth_device_name.GetEndpointInfo());
|
||||
EXPECT_EQ(kWebRtcState, bluetooth_device_name.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest,
|
||||
SerializationDeserializationWorksWithEmptyEndpointName) {
|
||||
std::string empty_endpoint_name;
|
||||
TEST(BluetoothDeviceNameTest, ConstructionWorksWithEmptyEndpointName) {
|
||||
ByteArray empty_endpoint_info;
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
empty_endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
empty_endpoint_name);
|
||||
ScopedPtr<Ptr<BluetoothDeviceName> > scoped_bluetooth_device_name(
|
||||
BluetoothDeviceName::fromString(bluetooth_device_name_string));
|
||||
|
||||
ASSERT_EQ(pcp, scoped_bluetooth_device_name->getPCP());
|
||||
ASSERT_EQ(version, scoped_bluetooth_device_name->getVersion());
|
||||
ASSERT_EQ(endpoint_id, scoped_bluetooth_device_name->getEndpointId());
|
||||
ASSERT_EQ(sizeof(service_id_hash_bytes) / sizeof(char),
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0,
|
||||
memcmp(service_id_hash_bytes,
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->getData(),
|
||||
scoped_bluetooth_device_name->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(empty_endpoint_name,
|
||||
scoped_bluetooth_device_name->getEndpointName());
|
||||
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_info, bluetooth_device_name.GetEndpointInfo());
|
||||
EXPECT_EQ(kWebRtcState, bluetooth_device_name.GetWebRtcState());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithBadVersion) {
|
||||
BluetoothDeviceName::Version::Value bad_version =
|
||||
static_cast<BluetoothDeviceName::Version::Value>(666);
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadVersion) {
|
||||
auto bad_version = static_cast<BluetoothDeviceName::Version>(666);
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{bad_version,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
bad_version, pcp, endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithBadPCP) {
|
||||
PCP::Value bad_pcp = static_cast<PCP::Value>(666);
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadPcp) {
|
||||
auto bad_pcp = static_cast<Pcp>(666);
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
bad_pcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, bad_pcp, endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithShortEndpointId) {
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortEndpointId) {
|
||||
std::string short_endpoint_id("AB1");
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
short_endpoint_id,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, short_endpoint_id,
|
||||
ConstifyPtr(scoped_service_id_hash.get()), endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithLongEndpointId) {
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongEndpointId) {
|
||||
std::string long_endpoint_id("AB12X");
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
long_endpoint_id,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, long_endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithShortServiceIdHash) {
|
||||
char short_service_id_hash_bytes[] = {0x0A, 0x0B};
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortServiceIdHash) {
|
||||
char short_service_id_hash_bytes[] = "\x0a\x0b";
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_short_service_id_hash(
|
||||
new ByteArray(short_service_id_hash_bytes,
|
||||
sizeof(short_service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray short_service_id_hash{short_service_id_hash_bytes};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
short_service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, endpoint_id,
|
||||
ConstifyPtr(scoped_short_service_id_hash.get()), endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, SerializationFailsWithLongServiceIdHash) {
|
||||
char long_service_id_hash_bytes[] = {0x0A, 0x0B, 0x0C, 0x0D};
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongServiceIdHash) {
|
||||
char long_service_id_hash_bytes[] = "\x0a\x0b\x0c\x0d";
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_long_service_id_hash(
|
||||
new ByteArray(long_service_id_hash_bytes,
|
||||
sizeof(long_service_id_hash_bytes) / sizeof(char)));
|
||||
ByteArray long_service_id_hash{long_service_id_hash_bytes};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
long_service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, endpoint_id, ConstifyPtr(scoped_long_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
|
||||
ASSERT_TRUE(bluetooth_device_name_string.empty());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, DeserializationFailsWithShortLength) {
|
||||
char bluetooth_device_name_bytes[] = {'X'};
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortStringLength) {
|
||||
char bluetooth_device_name_string[] = "X";
|
||||
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_bluetooth_device_name_bytes(
|
||||
new ByteArray(bluetooth_device_name_bytes,
|
||||
sizeof(bluetooth_device_name_bytes) / sizeof(char)));
|
||||
ByteArray bluetooth_device_name_bytes{bluetooth_device_name_string};
|
||||
BluetoothDeviceName bluetooth_device_name{
|
||||
Base64Utils::Encode(bluetooth_device_name_bytes)};
|
||||
|
||||
ScopedPtr<Ptr<BluetoothDeviceName> > scoped_bluetooth_device_name(
|
||||
BluetoothDeviceName::fromString(Base64Utils::encode(
|
||||
ConstifyPtr(scoped_bluetooth_device_name_bytes.get()))));
|
||||
|
||||
ASSERT_TRUE(scoped_bluetooth_device_name.isNull());
|
||||
EXPECT_FALSE(bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, DeserializationFailsWithWrongEndpointNameLength) {
|
||||
TEST(BluetoothDeviceNameTest, ConstructionFailsWithWrongEndpointNameLength) {
|
||||
// Serialize good data into a good Bluetooth Device Name.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_service_id_hash(new ByteArray(
|
||||
service_id_hash_bytes, sizeof(service_id_hash_bytes) / sizeof(char)));
|
||||
|
||||
std::string bluetooth_device_name_string = BluetoothDeviceName::asString(
|
||||
version, pcp, endpoint_id, ConstifyPtr(scoped_service_id_hash.get()),
|
||||
endpoint_name);
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
BluetoothDeviceName bluetooth_device_name{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
auto bluetooth_device_name_string = std::string(bluetooth_device_name);
|
||||
|
||||
// Base64-decode the good Bluetooth Device Name.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_bluetooth_device_name_bytes(
|
||||
Base64Utils::decode(bluetooth_device_name_string));
|
||||
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_bluetooth_device_name_bytes(
|
||||
scoped_bluetooth_device_name_bytes->getData(),
|
||||
scoped_bluetooth_device_name_bytes->size());
|
||||
corrupt_bluetooth_device_name_bytes[15] ^= 0x0FF;
|
||||
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.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_corrupt_bluetooth_device_name_bytes(
|
||||
new ByteArray(corrupt_bluetooth_device_name_bytes.data(),
|
||||
corrupt_bluetooth_device_name_bytes.size()));
|
||||
std::string corrupt_bluetooth_device_name_string(Base64Utils::encode(
|
||||
ConstifyPtr(scoped_corrupt_bluetooth_device_name_bytes.get())));
|
||||
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.
|
||||
ScopedPtr<Ptr<BluetoothDeviceName> > scoped_bluetooth_device_name(
|
||||
BluetoothDeviceName::fromString(corrupt_bluetooth_device_name_string));
|
||||
BluetoothDeviceName corrupt_bluetooth_device_name(
|
||||
corrupt_bluetooth_device_name_string);
|
||||
|
||||
ASSERT_TRUE(scoped_bluetooth_device_name.isNull());
|
||||
EXPECT_FALSE(corrupt_bluetooth_device_name.IsValid());
|
||||
}
|
||||
|
||||
TEST(BluetoothDeviceNameTest, CanParseGeneratedName) {
|
||||
ByteArray service_id_hash{std::string(kServiceIDHashBytes)};
|
||||
ByteArray endpoint_info{std::string(kEndPointName)};
|
||||
// Build name1 from scratch.
|
||||
BluetoothDeviceName name1{kVersion,
|
||||
kPcp,
|
||||
kEndPointID,
|
||||
service_id_hash,
|
||||
endpoint_info,
|
||||
ByteArray{},
|
||||
kWebRtcState};
|
||||
// Build name2 from string composed from name1.
|
||||
BluetoothDeviceName name2{std::string(name1)};
|
||||
EXPECT_TRUE(name1.IsValid());
|
||||
EXPECT_TRUE(name2.IsValid());
|
||||
EXPECT_EQ(name1.GetVersion(), name2.GetVersion());
|
||||
EXPECT_EQ(name1.GetPcp(), name2.GetPcp());
|
||||
EXPECT_EQ(name1.GetEndpointId(), name2.GetEndpointId());
|
||||
EXPECT_EQ(name1.GetServiceIdHash(), name2.GetServiceIdHash());
|
||||
EXPECT_EQ(name1.GetEndpointInfo(), name2.GetEndpointInfo());
|
||||
EXPECT_EQ(name1.GetWebRtcState(), name2.GetWebRtcState());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -2,40 +2,41 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "platform/public/bluetooth_classic.h"
|
||||
#include "platform/public/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
Ptr<BluetoothEndpointChannel> BluetoothEndpointChannel::createOutgoing(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BluetoothSocket> bluetooth_socket) {
|
||||
return MakePtr(new BluetoothEndpointChannel(channel_name, bluetooth_socket));
|
||||
namespace {
|
||||
|
||||
OutputStream* GetOutputStreamOrNull(BluetoothSocket& socket) {
|
||||
if (socket.GetRemoteDevice().IsValid()) return &socket.GetOutputStream();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<BluetoothEndpointChannel> BluetoothEndpointChannel::createIncoming(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BluetoothSocket> bluetooth_socket) {
|
||||
return MakePtr(new BluetoothEndpointChannel(channel_name, bluetooth_socket));
|
||||
InputStream* GetInputStreamOrNull(BluetoothSocket& socket) {
|
||||
if (socket.GetRemoteDevice().IsValid()) return &socket.GetInputStream();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BluetoothEndpointChannel::BluetoothEndpointChannel(
|
||||
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket)
|
||||
: BaseEndpointChannel(channel_name, bluetooth_socket->getInputStream(),
|
||||
bluetooth_socket->getOutputStream()),
|
||||
bluetooth_socket_(bluetooth_socket) {}
|
||||
const std::string& channel_name, BluetoothSocket socket)
|
||||
: BaseEndpointChannel(channel_name, GetInputStreamOrNull(socket),
|
||||
GetOutputStreamOrNull(socket)),
|
||||
bluetooth_socket_(std::move(socket)) {}
|
||||
|
||||
BluetoothEndpointChannel::~BluetoothEndpointChannel() {}
|
||||
|
||||
proto::connections::Medium BluetoothEndpointChannel::getMedium() {
|
||||
proto::connections::Medium BluetoothEndpointChannel::GetMedium() const {
|
||||
return proto::connections::Medium::BLUETOOTH;
|
||||
}
|
||||
|
||||
void BluetoothEndpointChannel::closeImpl() {
|
||||
Exception::Value exception = bluetooth_socket_->close();
|
||||
if (exception != Exception::NONE) {
|
||||
if (exception == Exception::IO) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
void BluetoothEndpointChannel::CloseImpl() {
|
||||
auto status = bluetooth_socket_.Close();
|
||||
if (!status.Ok()) {
|
||||
NEARBY_LOG(INFO, "Failed to close BT socket: exception=%d", status.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,28 @@
|
||||
#ifndef CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_
|
||||
#define CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/base_endpoint_channel.h"
|
||||
#include "core/internal/medium_manager.h"
|
||||
#include "platform/api/bluetooth_classic.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/public/bluetooth_classic.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
class BluetoothEndpointChannel : public BaseEndpointChannel {
|
||||
class BluetoothEndpointChannel final : public BaseEndpointChannel {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
// Creates both outgoing and incoming BT channels.
|
||||
BluetoothEndpointChannel(const std::string& channel_name,
|
||||
BluetoothSocket bluetooth_socket);
|
||||
|
||||
static Ptr<BluetoothEndpointChannel> createOutgoing(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BluetoothSocket> bluetooth_socket);
|
||||
static Ptr<BluetoothEndpointChannel> createIncoming(
|
||||
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
|
||||
Ptr<BluetoothSocket> bluetooth_socket);
|
||||
|
||||
~BluetoothEndpointChannel() override;
|
||||
|
||||
proto::connections::Medium getMedium() override;
|
||||
|
||||
protected:
|
||||
void closeImpl() override;
|
||||
proto::connections::Medium GetMedium() const override;
|
||||
|
||||
private:
|
||||
BluetoothEndpointChannel(const string& channel_name,
|
||||
Ptr<BluetoothSocket> bluetooth_socket);
|
||||
void CloseImpl() override;
|
||||
|
||||
ScopedPtr<Ptr<BluetoothSocket> > bluetooth_socket_;
|
||||
BluetoothSocket bluetooth_socket_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef CORE_V2_INTERNAL_BWU_HANDLER_H_
|
||||
#define CORE_V2_INTERNAL_BWU_HANDLER_H_
|
||||
#ifndef CORE_INTERNAL_BWU_HANDLER_H_
|
||||
#define CORE_INTERNAL_BWU_HANDLER_H_
|
||||
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_channel.h"
|
||||
#include "core_v2/internal/offline_frames.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
|
||||
namespace location {
|
||||
@@ -71,4 +71,4 @@ class BwuHandler {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_BWU_HANDLER_H_
|
||||
#endif // CORE_INTERNAL_BWU_HANDLER_H_
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "core_v2/internal/bwu_manager.h"
|
||||
#include "core/internal/bwu_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "core_v2/internal/bwu_handler.h"
|
||||
#include "core_v2/internal/offline_frames.h"
|
||||
#include "core_v2/internal/webrtc_bwu_handler.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "core/internal/bwu_handler.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "core/internal/webrtc_bwu_handler.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/functional/bind_front.h"
|
||||
#include "absl/time/time.h"
|
||||
@@ -653,6 +653,9 @@ std::vector<Medium> BwuManager::StripOutUnavailableMediums(
|
||||
case Medium::WIFI_LAN:
|
||||
available = mediums_->GetWifiLan().IsAvailable();
|
||||
break;
|
||||
case Medium::WEB_RTC:
|
||||
available = mediums_->GetWebRtc().IsAvailable();
|
||||
break;
|
||||
case Medium::BLUETOOTH:
|
||||
available = mediums_->GetBluetoothClassic().IsAvailable();
|
||||
break;
|
||||
@@ -1,18 +1,18 @@
|
||||
#ifndef CORE_V2_INTERNAL_BWU_MANAGER_H_
|
||||
#define CORE_V2_INTERNAL_BWU_MANAGER_H_
|
||||
#ifndef CORE_INTERNAL_BWU_MANAGER_H_
|
||||
#define CORE_INTERNAL_BWU_MANAGER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core_v2/internal/bwu_handler.h"
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_manager.h"
|
||||
#include "core_v2/internal/mediums/mediums.h"
|
||||
#include "core_v2/options.h"
|
||||
#include "core/internal/bwu_handler.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_manager.h"
|
||||
#include "core/internal/mediums/mediums.h"
|
||||
#include "core/options.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/scheduled_executor.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/scheduled_executor.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
@@ -175,4 +175,4 @@ class BwuManager : public EndpointManager::FrameProcessor {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_BWU_MANAGER_H_
|
||||
#endif // CORE_INTERNAL_BWU_MANAGER_H_
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "core_v2/internal/bwu_manager.h"
|
||||
#include "core/internal/bwu_manager.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_channel_manager.h"
|
||||
#include "core_v2/internal/endpoint_manager.h"
|
||||
#include "core_v2/internal/mediums/mediums.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/endpoint_manager.h"
|
||||
#include "core/internal/mediums/mediums.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
+401
-466
@@ -2,587 +2,522 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "platform/api/hash_utils.h"
|
||||
#include "platform/base64_utils.h"
|
||||
#include "platform/prng.h"
|
||||
#include "platform/synchronized.h"
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "platform/base/prng.h"
|
||||
#include "platform/public/crypto.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace client_proxy {
|
||||
ClientProxy::ClientProxy() : client_id_(Prng().NextInt64()) {}
|
||||
|
||||
template <typename K, typename V>
|
||||
void eraseOwnedPtrFromMap(std::map<K, Ptr<V>>& m, const K& k) {
|
||||
typename std::map<K, Ptr<V>>::iterator it = m.find(k);
|
||||
if (it != m.end()) {
|
||||
it->second.destroy();
|
||||
m.erase(it);
|
||||
ClientProxy::~ClientProxy() { Reset(); }
|
||||
|
||||
std::int64_t ClientProxy::GetClientId() const { return client_id_; }
|
||||
|
||||
std::string ClientProxy::GetLocalEndpointId() {
|
||||
if (local_endpoint_id_.empty()) {
|
||||
// 1) Concatenate the Random 64-bit value with "client" string.
|
||||
// 2) Compute a hash of that concatenation.
|
||||
// 3) Base64-encode that hash, to make it human-readable.
|
||||
// 4) Use only the first kEndpointIdLength bytes to make ID.
|
||||
ByteArray id_hash =
|
||||
Crypto::Sha256(absl::StrCat("client", prng_.NextInt64()));
|
||||
std::string id = Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength);
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Local Endpoint Generated]: client=%p; endpoint_id=%s",
|
||||
this, id.c_str());
|
||||
local_endpoint_id_ = id;
|
||||
}
|
||||
return local_endpoint_id_;
|
||||
}
|
||||
|
||||
} // namespace client_proxy
|
||||
void ClientProxy::Reset() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t ClientProxy<Platform>::kEndpointIdLength = 4;
|
||||
|
||||
template <typename Platform>
|
||||
ClientProxy<Platform>::ClientProxy()
|
||||
: lock_(Platform::createLock()), client_id_(Prng().nextInt64()) {}
|
||||
|
||||
template <typename Platform>
|
||||
ClientProxy<Platform>::~ClientProxy() {
|
||||
reset();
|
||||
StoppedAdvertising();
|
||||
StoppedDiscovery();
|
||||
RemoveAllEndpoints();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::int64_t ClientProxy<Platform>::getClientId() const {
|
||||
return client_id_;
|
||||
void ClientProxy::StartedAdvertising(
|
||||
const std::string& service_id, Strategy strategy,
|
||||
const ConnectionListener& listener,
|
||||
absl::Span<proto::connections::Medium> mediums) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (connections_.empty()) local_endpoint_id_.clear();
|
||||
advertising_info_ = {service_id, listener};
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::string ClientProxy<Platform>::generateLocalEndpointId() {
|
||||
// 1) Concatenate the DeviceID with this ClientID.
|
||||
// 2) Compute a hash of that concatenation.
|
||||
// 3) Base64-encode that hash, to make it human-readable.
|
||||
// 4) Use only the first 4 bytes of that Base64 encoding.
|
||||
void ClientProxy::StoppedAdvertising() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
std::ostringstream client_id_str;
|
||||
client_id_str << getClientId();
|
||||
|
||||
ScopedPtr<Ptr<HashUtils>> hash_utils(Platform::createHashUtils());
|
||||
ScopedPtr<ConstPtr<ByteArray>> id_hash(
|
||||
hash_utils->sha256(Platform::getDeviceId() + client_id_str.str()));
|
||||
|
||||
return Base64Utils::encode(id_hash.get()).substr(0, kEndpointIdLength);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::reset() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
stoppedAdvertising();
|
||||
stoppedDiscovery();
|
||||
removeAllEndpoints();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::startedAdvertising(
|
||||
const std::string& service_id, const Strategy& strategy,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener,
|
||||
const std::vector<proto::connections::Medium>& mediums) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
advertising_info_.destroy();
|
||||
advertising_info_ =
|
||||
MakePtr(new AdvertisingInfo(service_id, connection_lifecycle_listener));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::stoppedAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (isAdvertising()) {
|
||||
advertising_info_.destroy();
|
||||
if (IsAdvertising()) {
|
||||
advertising_info_.Clear();
|
||||
}
|
||||
if (connections_.empty()) local_endpoint_id_.clear();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
bool ClientProxy::IsAdvertising() const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !advertising_info_.isNull();
|
||||
return !advertising_info_.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::string ClientProxy<Platform>::getAdvertisingServiceId() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!isAdvertising()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return advertising_info_->service_id;
|
||||
std::string ClientProxy::GetAdvertisingServiceId() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return advertising_info_.service_id;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::startedDiscovery(
|
||||
const std::string& service_id, const Strategy& strategy,
|
||||
Ptr<DiscoveryListener> discovery_listener,
|
||||
const std::vector<proto::connections::Medium>& mediums) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
discovery_info_.destroy();
|
||||
discovery_info_ = MakePtr(new DiscoveryInfo(service_id, discovery_listener));
|
||||
std::string ClientProxy::GetServiceId() const {
|
||||
MutexLock lock(&mutex_);
|
||||
if (IsAdvertising())
|
||||
return advertising_info_.service_id;
|
||||
if (IsDiscovering())
|
||||
return discovery_info_.service_id;
|
||||
return "idle_service_id";
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::stoppedDiscovery() {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::StartedDiscovery(
|
||||
const std::string& service_id, Strategy strategy,
|
||||
const DiscoveryListener& listener,
|
||||
absl::Span<proto::connections::Medium> mediums) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (isDiscovering()) {
|
||||
if (connections_.empty()) local_endpoint_id_.clear();
|
||||
discovery_info_ = DiscoveryInfo{service_id, listener};
|
||||
}
|
||||
|
||||
void ClientProxy::StoppedDiscovery() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (IsDiscovering()) {
|
||||
discovered_endpoint_ids_.clear();
|
||||
discovery_info_.destroy();
|
||||
discovery_info_.Clear();
|
||||
}
|
||||
if (connections_.empty()) local_endpoint_id_.clear();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isDiscoveringServiceId(
|
||||
const std::string& service_id) {
|
||||
Synchronized s(lock_.get());
|
||||
bool ClientProxy::IsDiscoveringServiceId(const std::string& service_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return isDiscovering() && service_id == discovery_info_->service_id;
|
||||
return IsDiscovering() && service_id == discovery_info_.service_id;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isDiscovering() {
|
||||
Synchronized s(lock_.get());
|
||||
bool ClientProxy::IsDiscovering() const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !discovery_info_.isNull();
|
||||
return !discovery_info_.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::string ClientProxy<Platform>::getDiscoveryServiceId() {
|
||||
Synchronized s(lock_.get());
|
||||
std::string ClientProxy::GetDiscoveryServiceId() const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isDiscovering()) {
|
||||
return "";
|
||||
return discovery_info_.service_id;
|
||||
}
|
||||
|
||||
void ClientProxy::OnEndpointFound(const std::string& service_id,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
proto::connections::Medium medium) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
NEARBY_LOG(INFO,
|
||||
"ClientProxy [Endpoint Found]: [enter] id=%s; service=%s; info=%s",
|
||||
endpoint_id.c_str(), service_id.c_str(),
|
||||
absl::BytesToHexString(endpoint_info.data()).c_str());
|
||||
if (!IsDiscoveringServiceId(service_id)) {
|
||||
NEARBY_LOG(INFO, "ClientProxy [Endpoint Found]: [no discovery] id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
return discovery_info_->service_id;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onEndpointFound(const std::string& endpoint_id,
|
||||
const std::string& service_id,
|
||||
const std::string& endpoint_name,
|
||||
proto::connections::Medium medium) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (isDiscoveringServiceId(service_id)) {
|
||||
if (discovered_endpoint_ids_.find(endpoint_id) !=
|
||||
discovered_endpoint_ids_.end()) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return;
|
||||
}
|
||||
discovered_endpoint_ids_.insert(endpoint_id);
|
||||
discovery_info_->discovery_listener->onEndpointFound(MakeConstPtr(
|
||||
new OnEndpointFoundParams(endpoint_id, service_id, endpoint_name)));
|
||||
if (discovered_endpoint_ids_.count(endpoint_id)) {
|
||||
NEARBY_LOG(INFO, "ClientProxy [Endpoint Found]: [duplicate] id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
discovered_endpoint_ids_.insert(endpoint_id);
|
||||
discovery_info_.listener.endpoint_found_cb(endpoint_id, endpoint_info,
|
||||
service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onEndpointLost(const std::string& service_id,
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::OnEndpointLost(const std::string& service_id,
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (isDiscoveringServiceId(service_id)) {
|
||||
std::set<std::string>::const_iterator it =
|
||||
discovered_endpoint_ids_.find(endpoint_id);
|
||||
if (it == discovered_endpoint_ids_.end()) {
|
||||
return;
|
||||
}
|
||||
discovered_endpoint_ids_.erase(it);
|
||||
discovery_info_->discovery_listener->onEndpointLost(
|
||||
MakeConstPtr(new OnEndpointLostParams(endpoint_id)));
|
||||
}
|
||||
if (!IsDiscoveringServiceId(service_id)) return;
|
||||
const auto it = discovered_endpoint_ids_.find(endpoint_id);
|
||||
if (it == discovered_endpoint_ids_.end()) return;
|
||||
discovered_endpoint_ids_.erase(it);
|
||||
discovery_info_.listener.endpoint_lost_cb(endpoint_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onConnectionInitiated(
|
||||
const std::string& endpoint_id, const std::string& endpoint_name,
|
||||
const std::string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token, bool is_incoming_connection,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_raw_authentication_token(
|
||||
raw_authentication_token);
|
||||
void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id,
|
||||
const ConnectionResponseInfo& info,
|
||||
const ConnectionOptions& options,
|
||||
const ConnectionListener& listener) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
// Whether this is incoming or outgoing, the local and remote endpoints both
|
||||
// still need to accept this connection, so set its establishment status to
|
||||
// PENDING.
|
||||
connection_establishment_statuses_.insert(
|
||||
std::make_pair(endpoint_id, ConnectionMetadata(is_incoming_connection)));
|
||||
|
||||
// Remember the ConnectionLifecycleListener for this endpoint.
|
||||
connection_lifecycle_listeners_.insert(
|
||||
std::make_pair(endpoint_id, connection_lifecycle_listener));
|
||||
|
||||
auto result = connections_.emplace(
|
||||
endpoint_id, Connection{
|
||||
.is_incoming = info.is_incoming_connection,
|
||||
.connection_listener = listener,
|
||||
.connection_options = options,
|
||||
});
|
||||
// Instead of using structured binding which is nice, but banned
|
||||
// (can not use c++17 features, until chromium does) we unpack manually.
|
||||
auto& pair_iter = result.first;
|
||||
bool inserted = result.second;
|
||||
NEARBY_LOG(INFO,
|
||||
"ClientProxy [Connection Initiated]: add Connection: client=%p, "
|
||||
"id=%s; inserted=%d",
|
||||
this, endpoint_id.c_str(), inserted);
|
||||
DCHECK(inserted);
|
||||
const Connection& item = pair_iter->second;
|
||||
// Notify the client.
|
||||
//
|
||||
// Note: we allow devices to connect to an advertiser even after it stops
|
||||
// advertising, so no need to check isAdvertising() here.
|
||||
connection_lifecycle_listeners_.find(endpoint_id)
|
||||
->second->onConnectionInitiated(
|
||||
MakeConstPtr(new OnConnectionInitiatedParams(
|
||||
endpoint_id, endpoint_name, authentication_token,
|
||||
scoped_raw_authentication_token.release(),
|
||||
is_incoming_connection)));
|
||||
// advertising, so no need to check IsAdvertising() here.
|
||||
item.connection_listener.initiated_cb(endpoint_id, info);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onConnectionResult(const std::string& endpoint_id,
|
||||
Status::Value status) {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::OnConnectionAccepted(const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!hasPendingConnectionToEndpoint(endpoint_id)) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
if (!HasPendingConnectionToEndpoint(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO, "ClientProxy [Connection Accepted]: no pending connection; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the client.
|
||||
connection_lifecycle_listeners_.find(endpoint_id)
|
||||
->second->onConnectionResult(
|
||||
MakeConstPtr(new OnConnectionResultParams(endpoint_id, status)));
|
||||
if (Status::SUCCESS == status) {
|
||||
// Mark ourselves as connected. Payloads should now be allowed.
|
||||
typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.find(endpoint_id);
|
||||
if (it != connection_establishment_statuses_.end()) {
|
||||
it->second.status = ConnectionEstablishmentStatus::CONNECTED;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, clean up.
|
||||
onDisconnected(endpoint_id, false /* notify */);
|
||||
Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->connection_listener.accepted_cb(endpoint_id);
|
||||
item->status = Connection::kConnected;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onBandwidthChanged(const std::string& endpoint_id,
|
||||
std::int32_t quality) {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::OnConnectionRejected(const std::string& endpoint_id,
|
||||
const Status& status) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
ConnectionLifecycleListenersMap::iterator it =
|
||||
connection_lifecycle_listeners_.find(endpoint_id);
|
||||
if (it != connection_lifecycle_listeners_.end()) {
|
||||
it->second->onBandwidthChanged(
|
||||
MakeConstPtr(new OnBandwidthChangedParams(endpoint_id, quality)));
|
||||
if (!HasPendingConnectionToEndpoint(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO, "ClientProxy [Connection Rejected]: no pending connection; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the client.
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->connection_listener.rejected_cb(endpoint_id, status);
|
||||
OnDisconnected(endpoint_id, false /* notify */);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onDisconnected(const std::string& endpoint_id,
|
||||
bool notify) {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::OnBandwidthChanged(const std::string& endpoint_id,
|
||||
Medium new_medium) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
connection_establishment_statuses_.erase(endpoint_id);
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->connection_listener.bandwidth_changed_cb(endpoint_id, new_medium);
|
||||
}
|
||||
}
|
||||
|
||||
client_proxy::eraseOwnedPtrFromMap(payload_listeners_, endpoint_id);
|
||||
void ClientProxy::OnDisconnected(const std::string& endpoint_id, bool notify) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
ConnectionLifecycleListenersMap::iterator it =
|
||||
connection_lifecycle_listeners_.find(endpoint_id);
|
||||
if (it != connection_lifecycle_listeners_.end()) {
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
if (notify) {
|
||||
it->second->onDisconnected(
|
||||
MakeConstPtr(new OnDisconnectedParams(endpoint_id)));
|
||||
item->connection_listener.disconnected_cb({endpoint_id});
|
||||
}
|
||||
it->second.destroy();
|
||||
connection_lifecycle_listeners_.erase(it);
|
||||
connections_.erase(endpoint_id);
|
||||
if (connections_.empty()) local_endpoint_id_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isConnectedToEndpoint(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
bool ClientProxy::ConnectionStatusMatches(const std::string& endpoint_id,
|
||||
Connection::Status status) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.find(endpoint_id);
|
||||
if (it == connection_establishment_statuses_.end()) {
|
||||
return false;
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
return item->status == status;
|
||||
}
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
return metadata.status == ConnectionEstablishmentStatus::CONNECTED;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::vector<std::string> ClientProxy<Platform>::getConnectedEndpoints() {
|
||||
Synchronized s(lock_.get());
|
||||
BooleanMediumSelector ClientProxy::GetUpgradeMediums(
|
||||
const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
return item->connection_options.allowed;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ClientProxy::IsConnectedToEndpoint(const std::string& endpoint_id) const {
|
||||
return ConnectionStatusMatches(endpoint_id, Connection::kConnected);
|
||||
}
|
||||
|
||||
std::vector<std::string> ClientProxy::GetMatchingEndpoints(
|
||||
std::function<bool(const Connection&)> pred) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
std::vector<std::string> connected_endpoints;
|
||||
|
||||
for (typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.begin();
|
||||
it != connection_establishment_statuses_.end(); it++) {
|
||||
const std::string& endpoint_id = it->first;
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
if (ConnectionEstablishmentStatus::CONNECTED == metadata.status) {
|
||||
for (const auto& pair : connections_) {
|
||||
const auto& endpoint_id = pair.first;
|
||||
const auto& connection = pair.second;
|
||||
if (pred(connection)) {
|
||||
connected_endpoints.push_back(endpoint_id);
|
||||
}
|
||||
}
|
||||
return connected_endpoints;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::vector<std::string> ClientProxy<Platform>::getPendingConnectedEndpoints() {
|
||||
Synchronized s(lock_.get());
|
||||
std::vector<std::string> ClientProxy::GetPendingConnectedEndpoints() const {
|
||||
return GetMatchingEndpoints([](const Connection& connection) {
|
||||
return connection.status != Connection::kConnected;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<std::string> pending_connected_endpoints;
|
||||
std::vector<std::string> ClientProxy::GetConnectedEndpoints() const {
|
||||
return GetMatchingEndpoints([](const Connection& connection) {
|
||||
return connection.status == Connection::kConnected;
|
||||
});
|
||||
}
|
||||
|
||||
for (typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.begin();
|
||||
it != connection_establishment_statuses_.end(); it++) {
|
||||
const std::string& endpoint_id = it->first;
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
if (ConnectionEstablishmentStatus::CONNECTED != metadata.status) {
|
||||
pending_connected_endpoints.push_back(endpoint_id);
|
||||
std::int32_t ClientProxy::GetNumOutgoingConnections() const {
|
||||
return GetMatchingEndpoints([](const Connection& connection) {
|
||||
return connection.status == Connection::kConnected &&
|
||||
!connection.is_incoming;
|
||||
})
|
||||
.size();
|
||||
}
|
||||
|
||||
std::int32_t ClientProxy::GetNumIncomingConnections() const {
|
||||
return GetMatchingEndpoints([](const Connection& connection) {
|
||||
return connection.status == Connection::kConnected &&
|
||||
connection.is_incoming;
|
||||
})
|
||||
.size();
|
||||
}
|
||||
|
||||
bool ClientProxy::HasPendingConnectionToEndpoint(
|
||||
const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
return item->status != Connection::kConnected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClientProxy::HasLocalEndpointResponded(
|
||||
const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return ConnectionStatusesContains(
|
||||
endpoint_id,
|
||||
static_cast<Connection::Status>(Connection::kLocalEndpointAccepted |
|
||||
Connection::kLocalEndpointRejected));
|
||||
}
|
||||
|
||||
bool ClientProxy::HasRemoteEndpointResponded(
|
||||
const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return ConnectionStatusesContains(
|
||||
endpoint_id,
|
||||
static_cast<Connection::Status>(Connection::kRemoteEndpointAccepted |
|
||||
Connection::kRemoteEndpointRejected));
|
||||
}
|
||||
|
||||
void ClientProxy::LocalEndpointAcceptedConnection(
|
||||
const std::string& endpoint_id, const PayloadListener& listener) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (HasLocalEndpointResponded(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Local Accepted]: local endpoint has responded; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AppendConnectionStatus(endpoint_id, Connection::kLocalEndpointAccepted);
|
||||
Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->payload_listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientProxy::LocalEndpointRejectedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (HasLocalEndpointResponded(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Local Rejected]: local endpoint has responded; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AppendConnectionStatus(endpoint_id, Connection::kLocalEndpointRejected);
|
||||
}
|
||||
|
||||
void ClientProxy::RemoteEndpointAcceptedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (HasRemoteEndpointResponded(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Remote Accepted]: remote endpoint has responded; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AppendConnectionStatus(endpoint_id, Connection::kRemoteEndpointAccepted);
|
||||
}
|
||||
|
||||
void ClientProxy::RemoteEndpointRejectedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (HasRemoteEndpointResponded(endpoint_id)) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Remote Rejected]: remote endpoint has responded; id=%s",
|
||||
endpoint_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AppendConnectionStatus(endpoint_id, Connection::kRemoteEndpointRejected);
|
||||
}
|
||||
|
||||
bool ClientProxy::IsConnectionAccepted(const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return ConnectionStatusesContains(endpoint_id,
|
||||
Connection::kLocalEndpointAccepted) &&
|
||||
ConnectionStatusesContains(endpoint_id,
|
||||
Connection::kRemoteEndpointAccepted);
|
||||
}
|
||||
|
||||
bool ClientProxy::IsConnectionRejected(const std::string& endpoint_id) const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return ConnectionStatusesContains(
|
||||
endpoint_id,
|
||||
static_cast<Connection::Status>(Connection::kLocalEndpointRejected |
|
||||
Connection::kRemoteEndpointRejected));
|
||||
}
|
||||
|
||||
bool ClientProxy::LocalConnectionIsAccepted(std::string endpoint_id) const {
|
||||
return ConnectionStatusesContains(
|
||||
endpoint_id, ClientProxy::Connection::kLocalEndpointAccepted);
|
||||
}
|
||||
|
||||
bool ClientProxy::RemoteConnectionIsAccepted(std::string endpoint_id) const {
|
||||
return ConnectionStatusesContains(
|
||||
endpoint_id, ClientProxy::Connection::kRemoteEndpointAccepted);
|
||||
}
|
||||
|
||||
void ClientProxy::OnPayload(const std::string& endpoint_id, Payload payload) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (IsConnectedToEndpoint(endpoint_id)) {
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->payload_listener.payload_cb(endpoint_id, std::move(payload));
|
||||
}
|
||||
}
|
||||
return pending_connected_endpoints;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::int32_t ClientProxy<Platform>::getNumOutgoingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
const ClientProxy::Connection* ClientProxy::LookupConnection(
|
||||
const std::string& endpoint_id) const {
|
||||
auto item = connections_.find(endpoint_id);
|
||||
return item != connections_.end() ? &item->second : nullptr;
|
||||
}
|
||||
|
||||
std::int32_t num_outgoing_connections = 0;
|
||||
ClientProxy::Connection* ClientProxy::LookupConnection(
|
||||
const std::string& endpoint_id) {
|
||||
auto item = connections_.find(endpoint_id);
|
||||
return item != connections_.end() ? &item->second : nullptr;
|
||||
}
|
||||
|
||||
for (typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.begin();
|
||||
it != connection_establishment_statuses_.end(); it++) {
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
if (ConnectionEstablishmentStatus::CONNECTED == metadata.status &&
|
||||
!metadata.is_incoming) {
|
||||
num_outgoing_connections++;
|
||||
void ClientProxy::OnPayloadProgress(const std::string& endpoint_id,
|
||||
const PayloadProgressInfo& info) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (IsConnectedToEndpoint(endpoint_id)) {
|
||||
Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->payload_listener.payload_progress_cb(endpoint_id, info);
|
||||
}
|
||||
}
|
||||
return num_outgoing_connections;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
std::int32_t ClientProxy<Platform>::getNumIncomingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
std::int32_t num_incoming_connections = 0;
|
||||
|
||||
for (typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.begin();
|
||||
it != connection_establishment_statuses_.end(); it++) {
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
if (ConnectionEstablishmentStatus::CONNECTED == metadata.status &&
|
||||
metadata.is_incoming) {
|
||||
num_incoming_connections++;
|
||||
}
|
||||
}
|
||||
return num_incoming_connections;
|
||||
bool operator==(const ClientProxy& lhs, const ClientProxy& rhs) {
|
||||
return lhs.GetClientId() == rhs.GetClientId();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::hasPendingConnectionToEndpoint(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.find(endpoint_id);
|
||||
if (it == connection_establishment_statuses_.end()) {
|
||||
return false;
|
||||
}
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
return metadata.status != ConnectionEstablishmentStatus::CONNECTED;
|
||||
bool operator<(const ClientProxy& lhs, const ClientProxy& rhs) {
|
||||
return lhs.GetClientId() < rhs.GetClientId();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::hasLocalEndpointResponded(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::LOCAL_ENDPOINT_ACCEPTED) ||
|
||||
connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::LOCAL_ENDPOINT_REJECTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::hasRemoteEndpointResponded(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::REMOTE_ENDPOINT_ACCEPTED) ||
|
||||
connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::REMOTE_ENDPOINT_REJECTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::localEndpointAcceptedConnection(
|
||||
const std::string& endpoint_id, Ptr<PayloadListener> payload_listener) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (hasLocalEndpointResponded(endpoint_id)) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return;
|
||||
}
|
||||
|
||||
appendConnectionEstablishmentStatus(
|
||||
endpoint_id, ConnectionEstablishmentStatus::LOCAL_ENDPOINT_ACCEPTED);
|
||||
payload_listeners_.insert(std::make_pair(endpoint_id, payload_listener));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::localEndpointRejectedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (hasLocalEndpointResponded(endpoint_id)) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return;
|
||||
}
|
||||
|
||||
appendConnectionEstablishmentStatus(
|
||||
endpoint_id, ConnectionEstablishmentStatus::LOCAL_ENDPOINT_REJECTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::remoteEndpointAcceptedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (hasRemoteEndpointResponded(endpoint_id)) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return;
|
||||
}
|
||||
|
||||
appendConnectionEstablishmentStatus(
|
||||
endpoint_id, ConnectionEstablishmentStatus::REMOTE_ENDPOINT_ACCEPTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::remoteEndpointRejectedConnection(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (hasRemoteEndpointResponded(endpoint_id)) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return;
|
||||
}
|
||||
|
||||
appendConnectionEstablishmentStatus(
|
||||
endpoint_id, ConnectionEstablishmentStatus::REMOTE_ENDPOINT_REJECTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isConnectionAccepted(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::LOCAL_ENDPOINT_ACCEPTED) &&
|
||||
connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::REMOTE_ENDPOINT_ACCEPTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::isConnectionRejected(
|
||||
const std::string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::LOCAL_ENDPOINT_REJECTED) ||
|
||||
connectionEstablishmentStatusesContains(
|
||||
endpoint_id,
|
||||
ConnectionEstablishmentStatus::REMOTE_ENDPOINT_REJECTED);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onPayloadReceived(const std::string& endpoint_id,
|
||||
ConstPtr<Payload> payload) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<Payload>> scoped_payload(payload);
|
||||
|
||||
if (isConnectedToEndpoint(endpoint_id)) {
|
||||
payload_listeners_.find(endpoint_id)
|
||||
->second->onPayloadReceived(MakeConstPtr(new OnPayloadReceivedParams(
|
||||
endpoint_id, scoped_payload.release())));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::onPayloadTransferUpdate(
|
||||
const std::string& endpoint_id,
|
||||
const PayloadTransferUpdate& payload_transfer_update) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (isConnectedToEndpoint(endpoint_id)) {
|
||||
payload_listeners_.find(endpoint_id)
|
||||
->second->onPayloadTransferUpdate(
|
||||
MakeConstPtr(new OnPayloadTransferUpdateParams(
|
||||
endpoint_id, payload_transfer_update)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::operator==(const ClientProxy<Platform>& rhs) {
|
||||
return this->getClientId() == rhs.getClientId();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::operator<(const ClientProxy<Platform>& rhs) {
|
||||
return this->getClientId() < rhs.getClientId();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::removeAllEndpoints() {
|
||||
Synchronized s(lock_.get());
|
||||
void ClientProxy::RemoveAllEndpoints() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
// Note: we may want to notify the client of onDisconnected() for each
|
||||
// endpoint, in the case when this is called from stopAllEndpoints(). For now,
|
||||
// just remove without notifying.
|
||||
for (ConnectionLifecycleListenersMap::iterator it =
|
||||
connection_lifecycle_listeners_.begin();
|
||||
it != connection_lifecycle_listeners_.end(); it++) {
|
||||
it->second.destroy();
|
||||
}
|
||||
connection_lifecycle_listeners_.clear();
|
||||
|
||||
for (PayloadListenersMap::iterator it = payload_listeners_.begin();
|
||||
it != payload_listeners_.end(); it++) {
|
||||
it->second.destroy();
|
||||
}
|
||||
payload_listeners_.clear();
|
||||
|
||||
connection_establishment_statuses_.clear();
|
||||
connections_.clear();
|
||||
local_endpoint_id_.clear();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool ClientProxy<Platform>::connectionEstablishmentStatusesContains(
|
||||
const std::string& endpoint_id,
|
||||
typename ConnectionEstablishmentStatus::Value status_to_match) {
|
||||
typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.find(endpoint_id);
|
||||
if (it == connection_establishment_statuses_.end()) {
|
||||
return false;
|
||||
bool ClientProxy::ConnectionStatusesContains(
|
||||
const std::string& endpoint_id, Connection::Status status_to_match) const {
|
||||
const Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
return (item->status & status_to_match) != 0;
|
||||
}
|
||||
const ConnectionMetadata& metadata = it->second;
|
||||
return (metadata.status & status_to_match) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void ClientProxy<Platform>::appendConnectionEstablishmentStatus(
|
||||
const std::string& endpoint_id,
|
||||
typename ConnectionEstablishmentStatus::Value status_to_append) {
|
||||
typename ConnectionEstablishmentStatusesMap::iterator it =
|
||||
connection_establishment_statuses_.find(endpoint_id);
|
||||
if (it == connection_establishment_statuses_.end()) {
|
||||
return;
|
||||
void ClientProxy::AppendConnectionStatus(const std::string& endpoint_id,
|
||||
Connection::Status status_to_append) {
|
||||
Connection* item = LookupConnection(endpoint_id);
|
||||
if (item != nullptr) {
|
||||
item->status =
|
||||
static_cast<Connection::Status>(item->status | status_to_append);
|
||||
}
|
||||
ConnectionMetadata& metadata = it->second;
|
||||
metadata.status = static_cast<typename ConnectionEstablishmentStatus::Value>(
|
||||
metadata.status | status_to_append);
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
+146
-159
@@ -2,240 +2,227 @@
|
||||
#define CORE_INTERNAL_CLIENT_PROXY_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#include "core/status.h"
|
||||
#include "core/strategy.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/prng.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
// Prefer using absl:: versions of a set and a map; they tend to be more
|
||||
// efficient: implementation is using open-addressing hash tables.
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
class ClientProxy {
|
||||
// CLientProxy is tracking state of client's connection, and serves as
|
||||
// a proxy for notifications sent to this client.
|
||||
class ClientProxy final {
|
||||
public:
|
||||
static const std::int32_t kEndpointIdLength;
|
||||
static constexpr int kEndpointIdLength = 4;
|
||||
|
||||
ClientProxy();
|
||||
~ClientProxy();
|
||||
ClientProxy(ClientProxy&&) = default;
|
||||
ClientProxy& operator=(ClientProxy&&) = default;
|
||||
|
||||
std::int64_t getClientId() const;
|
||||
std::int64_t GetClientId() const;
|
||||
|
||||
std::string generateLocalEndpointId();
|
||||
std::string GetLocalEndpointId();
|
||||
|
||||
// Clears all the runtime state of this client.
|
||||
void reset();
|
||||
void Reset();
|
||||
|
||||
// Marks this client as advertising with the given callbacks.
|
||||
void startedAdvertising(
|
||||
const std::string& service_id, const Strategy& strategy,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener,
|
||||
const std::vector<proto::connections::Medium>& mediums);
|
||||
void StartedAdvertising(
|
||||
const std::string& service_id, Strategy strategy,
|
||||
const ConnectionListener& connection_lifecycle_listener,
|
||||
absl::Span<proto::connections::Medium> mediums);
|
||||
// Marks this client as not advertising.
|
||||
void stoppedAdvertising();
|
||||
bool isAdvertising();
|
||||
std::string getAdvertisingServiceId();
|
||||
void StoppedAdvertising();
|
||||
bool IsAdvertising() const;
|
||||
std::string GetAdvertisingServiceId() const;
|
||||
|
||||
// Get service ID of a surrently active link (either advertising, or
|
||||
// discovering).
|
||||
std::string GetServiceId() const;
|
||||
|
||||
// Marks this client as discovering with the given callback.
|
||||
void startedDiscovery(const std::string& service_id, const Strategy& strategy,
|
||||
Ptr<DiscoveryListener> discovery_listener,
|
||||
const std::vector<proto::connections::Medium>& mediums);
|
||||
void StartedDiscovery(const std::string& service_id, Strategy strategy,
|
||||
const DiscoveryListener& discovery_listener,
|
||||
absl::Span<proto::connections::Medium> mediums);
|
||||
// Marks this client as not discovering at all.
|
||||
void stoppedDiscovery();
|
||||
bool isDiscoveringServiceId(const std::string& service_id);
|
||||
bool isDiscovering();
|
||||
std::string getDiscoveryServiceId();
|
||||
void StoppedDiscovery();
|
||||
bool IsDiscoveringServiceId(const std::string& service_id) const;
|
||||
bool IsDiscovering() const;
|
||||
std::string GetDiscoveryServiceId() const;
|
||||
|
||||
// Proxies to the client's DiscoveryListener.onEndpointFound() callback.
|
||||
void onEndpointFound(const std::string& endpoint_id,
|
||||
const std::string& service_id,
|
||||
const std::string& endpoint_name,
|
||||
// Proxies to the client's DiscoveryListener::OnEndpointFound() callback.
|
||||
void OnEndpointFound(const std::string& service_id,
|
||||
const std::string& endpoint_id,
|
||||
const ByteArray& endpoint_info,
|
||||
proto::connections::Medium medium);
|
||||
// Proxies to the client's DiscoveryListener.onEndpointLost() callback.
|
||||
void onEndpointLost(const std::string& service_id,
|
||||
// Proxies to the client's DiscoveryListener::OnEndpointLost() callback.
|
||||
void OnEndpointLost(const std::string& service_id,
|
||||
const std::string& endpoint_id);
|
||||
|
||||
// Proxies to the client's ConnectionLifecycleListener.onConnectionInitiated()
|
||||
// callback.
|
||||
void onConnectionInitiated(
|
||||
const std::string& endpoint_id, const std::string& endpoint_name,
|
||||
const std::string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token, bool is_incoming_connection,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener);
|
||||
// Proxies to the client's ConnectionLifecycleListener.onConnectionResult()
|
||||
// callback.
|
||||
void onConnectionResult(const std::string& endpoint_id, Status::Value status);
|
||||
// Proxies to the client's ConnectionListener::OnInitiated() callback.
|
||||
void OnConnectionInitiated(const std::string& endpoint_id,
|
||||
const ConnectionResponseInfo& info,
|
||||
const ConnectionOptions& options,
|
||||
const ConnectionListener& listener);
|
||||
|
||||
void onBandwidthChanged(const std::string& endpoint_id, std::int32_t quality);
|
||||
// Proxies to the client's ConnectionListener::OnAccepted() callback.
|
||||
void OnConnectionAccepted(const std::string& endpoint_id);
|
||||
// Proxies to the client's ConnectionListener::OnRejected() callback.
|
||||
void OnConnectionRejected(const std::string& endpoint_id,
|
||||
const Status& status);
|
||||
|
||||
void OnBandwidthChanged(const std::string& endpoint_id, Medium new_medium);
|
||||
|
||||
// Removes the endpoint from this client's list of connected endpoints. If
|
||||
// notify is true, also calls the client's
|
||||
// ConnectionLifecycleListener.onDisconnected() callback.
|
||||
void onDisconnected(const std::string& endpoint_id, bool notify);
|
||||
// ConnectionListener.disconnected_cb() callback.
|
||||
void OnDisconnected(const std::string& endpoint_id, bool notify);
|
||||
|
||||
// Returns all mediums eligible for upgrade.
|
||||
BooleanMediumSelector GetUpgradeMediums(const std::string& endpoint_id) const;
|
||||
// Returns true if it's safe to send payloads to this endpoint.
|
||||
bool isConnectedToEndpoint(const std::string& endpoint_id);
|
||||
bool IsConnectedToEndpoint(const std::string& endpoint_id) const;
|
||||
// Returns all endpoints that can safely be sent payloads.
|
||||
std::vector<std::string> getConnectedEndpoints();
|
||||
std::vector<std::string> GetConnectedEndpoints() const;
|
||||
// Returns all endpoints that are still awaiting acceptance.
|
||||
std::vector<std::string> getPendingConnectedEndpoints();
|
||||
std::vector<std::string> GetPendingConnectedEndpoints() const;
|
||||
// Returns the number of endpoints that are connected and outgoing.
|
||||
std::int32_t getNumOutgoingConnections();
|
||||
std::int32_t GetNumOutgoingConnections() const;
|
||||
// Returns the number of endpoints that are connected and incoming.
|
||||
std::int32_t getNumIncomingConnections();
|
||||
std::int32_t GetNumIncomingConnections() const;
|
||||
// If true, then we're in the process of approving (or rejecting) a
|
||||
// connection. No payloads should be sent until isConnectedToEndpoint()
|
||||
// returns true.
|
||||
bool hasPendingConnectionToEndpoint(const std::string& endpoint_id);
|
||||
bool HasPendingConnectionToEndpoint(const std::string& endpoint_id) const;
|
||||
// Returns true if the local endpoint has already marked itself as
|
||||
// accepted/rejected.
|
||||
bool hasLocalEndpointResponded(const std::string& endpoint_id);
|
||||
bool HasLocalEndpointResponded(const std::string& endpoint_id) const;
|
||||
// Returns true if the remote endpoint has already marked themselves as
|
||||
// accepted/rejected.
|
||||
bool hasRemoteEndpointResponded(const std::string& endpoint_id);
|
||||
bool HasRemoteEndpointResponded(const std::string& endpoint_id) const;
|
||||
// Marks the local endpoint as having accepted the connection.
|
||||
void localEndpointAcceptedConnection(const std::string& endpoint_id,
|
||||
Ptr<PayloadListener> payload_listener);
|
||||
void LocalEndpointAcceptedConnection(const std::string& endpoint_id,
|
||||
const PayloadListener& listener);
|
||||
// Marks the local endpoint as having rejected the connection.
|
||||
void localEndpointRejectedConnection(const std::string& endpoint_id);
|
||||
void LocalEndpointRejectedConnection(const std::string& endpoint_id);
|
||||
// Marks the remote endpoint as having accepted the connection.
|
||||
void remoteEndpointAcceptedConnection(const std::string& endpoint_id);
|
||||
void RemoteEndpointAcceptedConnection(const std::string& endpoint_id);
|
||||
// Marks the remote endpoint as having rejected the connection.
|
||||
void remoteEndpointRejectedConnection(const std::string& endpoint_id);
|
||||
void RemoteEndpointRejectedConnection(const std::string& endpoint_id);
|
||||
// Returns true if both the local endpoint and the remote endpoint have
|
||||
// accepted the connection.
|
||||
bool isConnectionAccepted(const std::string& endpoint_id);
|
||||
bool IsConnectionAccepted(const std::string& endpoint_id) const;
|
||||
// Returns true if either the local endpoint or the remote endpoint has
|
||||
// rejected the connection.
|
||||
bool isConnectionRejected(const std::string& endpoint_id);
|
||||
bool IsConnectionRejected(const std::string& endpoint_id) const;
|
||||
|
||||
// Proxies to the client's PayloadListener.onPayloadReceived() callback.
|
||||
void onPayloadReceived(const std::string& endpoint_id,
|
||||
ConstPtr<Payload> payload);
|
||||
// Proxies to the client's PayloadListener.onPayloadTransferUpdate() callback.
|
||||
void onPayloadTransferUpdate(
|
||||
const std::string& endpoint_id,
|
||||
const PayloadTransferUpdate& payload_transfer_update);
|
||||
|
||||
// Operator overloads when comparing Ptr<ClientProxy>.
|
||||
bool operator==(const ClientProxy<Platform>& rhs);
|
||||
bool operator<(const ClientProxy<Platform>& rhs);
|
||||
// Proxies to the client's PayloadListener::OnPayload() callback.
|
||||
void OnPayload(const std::string& endpoint_id, Payload payload);
|
||||
// Proxies to the client's PayloadListener::OnPayloadProgress() callback.
|
||||
void OnPayloadProgress(const std::string& endpoint_id,
|
||||
const PayloadProgressInfo& info);
|
||||
bool LocalConnectionIsAccepted(std::string endpoint_id) const;
|
||||
bool RemoteConnectionIsAccepted(std::string endpoint_id) const;
|
||||
|
||||
private:
|
||||
struct ConnectionEstablishmentStatus {
|
||||
enum Value {
|
||||
PENDING = 0,
|
||||
LOCAL_ENDPOINT_ACCEPTED = 1 << 0,
|
||||
LOCAL_ENDPOINT_REJECTED = 1 << 1,
|
||||
REMOTE_ENDPOINT_ACCEPTED = 1 << 2,
|
||||
REMOTE_ENDPOINT_REJECTED = 1 << 3,
|
||||
CONNECTED = 1 << 4,
|
||||
struct Connection {
|
||||
// Status: may be either:
|
||||
// Connection::PENDING, or combination of
|
||||
// Connection::LOCAL_ENDPOINT_ACCEPTED:
|
||||
// Connection::LOCAL_ENDPOINT_REJECTED and
|
||||
// Connection::REMOTE_ENDPOINT_ACCEPTED:
|
||||
// Connection::REMOTE_ENDPOINT_REJECTED, or
|
||||
// Connection::CONNECTED.
|
||||
// Only when this is set to CONNECTED should you allow payload transfers.
|
||||
//
|
||||
// We want this enum to be implicitly convertible to int, because
|
||||
// we perform bit operations on it.
|
||||
enum Status : uint8_t {
|
||||
kPending = 0,
|
||||
kLocalEndpointAccepted = 1 << 0,
|
||||
kLocalEndpointRejected = 1 << 1,
|
||||
kRemoteEndpointAccepted = 1 << 2,
|
||||
kRemoteEndpointRejected = 1 << 3,
|
||||
kConnected = 1 << 4,
|
||||
};
|
||||
bool is_incoming{false};
|
||||
Status status{kPending};
|
||||
ConnectionListener connection_listener;
|
||||
PayloadListener payload_listener;
|
||||
ConnectionOptions connection_options;
|
||||
};
|
||||
|
||||
struct AdvertisingInfo {
|
||||
const std::string service_id;
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener;
|
||||
|
||||
AdvertisingInfo(
|
||||
const std::string& service_id,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener)
|
||||
: service_id(service_id),
|
||||
connection_lifecycle_listener(connection_lifecycle_listener) {}
|
||||
std::string service_id;
|
||||
ConnectionListener listener;
|
||||
void Clear() { service_id.clear(); }
|
||||
bool IsEmpty() const { return service_id.empty(); }
|
||||
};
|
||||
|
||||
struct DiscoveryInfo {
|
||||
const std::string service_id;
|
||||
ScopedPtr<Ptr<DiscoveryListener> > discovery_listener;
|
||||
|
||||
DiscoveryInfo(const std::string& service_id,
|
||||
Ptr<DiscoveryListener> discovery_listener)
|
||||
: service_id(service_id), discovery_listener(discovery_listener) {}
|
||||
std::string service_id;
|
||||
DiscoveryListener listener;
|
||||
void Clear() { service_id.clear(); }
|
||||
bool IsEmpty() const { return service_id.empty(); }
|
||||
};
|
||||
|
||||
struct ConnectionMetadata {
|
||||
const bool is_incoming;
|
||||
typename ConnectionEstablishmentStatus::Value status;
|
||||
void RemoveAllEndpoints();
|
||||
bool ConnectionStatusesContains(const std::string& endpoint_id,
|
||||
Connection::Status status_to_match) const;
|
||||
void AppendConnectionStatus(const std::string& endpoint_id,
|
||||
Connection::Status status_to_append);
|
||||
|
||||
explicit ConnectionMetadata(bool is_incoming)
|
||||
: is_incoming(is_incoming),
|
||||
status(ConnectionEstablishmentStatus::PENDING) {}
|
||||
};
|
||||
const Connection* LookupConnection(const std::string& endpoint_id) const;
|
||||
Connection* LookupConnection(const std::string& endpoint_id);
|
||||
bool ConnectionStatusMatches(const std::string& endpoint_id,
|
||||
Connection::Status status) const;
|
||||
std::vector<std::string> GetMatchingEndpoints(
|
||||
std::function<bool(const Connection&)> pred) const;
|
||||
|
||||
void removeAllEndpoints();
|
||||
mutable RecursiveMutex mutex_;
|
||||
std::int64_t client_id_;
|
||||
std::string local_endpoint_id_;
|
||||
Prng prng_;
|
||||
|
||||
bool connectionEstablishmentStatusesContains(
|
||||
const std::string& endpoint_id,
|
||||
typename ConnectionEstablishmentStatus::Value status_to_match);
|
||||
void appendConnectionEstablishmentStatus(
|
||||
const std::string& endpoint_id,
|
||||
typename ConnectionEstablishmentStatus::Value status_to_append);
|
||||
// If not empty, we are currently advertising and accepting connection
|
||||
// requests for the given service_id.
|
||||
AdvertisingInfo advertising_info_;
|
||||
|
||||
ScopedPtr<Ptr<Lock> > lock_;
|
||||
const std::int64_t client_id_;
|
||||
// If not empty, we are currently discovering for the given service_id.
|
||||
DiscoveryInfo discovery_info_;
|
||||
|
||||
// If set, we are currently advertising and accepting connection requests for
|
||||
// the given service_id.
|
||||
Ptr<AdvertisingInfo> advertising_info_;
|
||||
// Maps endpoint_id to endpoint connection state.
|
||||
absl::flat_hash_map<std::string, Connection> connections_;
|
||||
|
||||
// If set, we are currently discovering for the given service_id.
|
||||
Ptr<DiscoveryInfo> discovery_info_;
|
||||
|
||||
/**
|
||||
* Map of endpoint_ids -> ConnectionMetadata. ConnectionMetadata.status may be
|
||||
* either ConnectionEstablishmentStatus::PENDING, a combination of
|
||||
* ConnectionEstablishmentStatus::LOCAL_ENDPOINT_ACCEPTED:
|
||||
* ConnectionEstablishmentStatus::LOCAL_ENDPOINT_REJECTED and
|
||||
* ConnectionEstablishmentStatus::REMOTE_ENDPOINT_ACCEPTED:
|
||||
* ConnectionEstablishmentStatus::REMOTE_ENDPOINT_REJECTED, or
|
||||
* ConnectionEstablishmentStatus::CONNECTED. Only when this is set to
|
||||
* CONNECTED should you allow payload transfers.
|
||||
*/
|
||||
typedef std::map<std::string, ConnectionMetadata>
|
||||
ConnectionEstablishmentStatusesMap;
|
||||
ConnectionEstablishmentStatusesMap connection_establishment_statuses_;
|
||||
|
||||
/**
|
||||
* Map of endpoint_ids -> ConnectionLifecycleListeners. Every endpoint in here
|
||||
* is guaranteed to at least be in
|
||||
* ConnectionEstablishmentStatus::PENDING -- the precise status can be found
|
||||
* from the corresponding entry in connection_establishment_statuses.
|
||||
*/
|
||||
typedef std::map<std::string, Ptr<ConnectionLifecycleListener> >
|
||||
ConnectionLifecycleListenersMap;
|
||||
ConnectionLifecycleListenersMap connection_lifecycle_listeners_;
|
||||
|
||||
/**
|
||||
* Map of endpoint_ids -> PayloadListeners. Every endpoint in here is
|
||||
* guaranteed to at least be in
|
||||
* ConnectionEstablishmentStatus::LOCAL_ENDPOINT_ACCEPTED -- the
|
||||
* precise status can be found from the corresponding entry in
|
||||
* connection_establishment_statuses.
|
||||
*/
|
||||
typedef std::map<std::string, Ptr<PayloadListener> > PayloadListenersMap;
|
||||
PayloadListenersMap payload_listeners_;
|
||||
|
||||
/**
|
||||
* A cache of endpoint ids that we've already notified the discoverer of. We
|
||||
* check this cache before calling onEndpointFound() so that we don't notify
|
||||
* the client multiple times for the same endpoint. This would otherwise
|
||||
* happen because some mediums (like Bluetooth) repeatedly give us the same
|
||||
* endpoints after each scan.
|
||||
*/
|
||||
std::set<std::string> discovered_endpoint_ids_;
|
||||
// A cache of endpoint ids that we've already notified the discoverer of. We
|
||||
// check this cache before calling onEndpointFound() so that we don't notify
|
||||
// the client multiple times for the same endpoint. This would otherwise
|
||||
// happen because some mediums (like Bluetooth) repeatedly give us the same
|
||||
// endpoints after each scan.
|
||||
absl::flat_hash_set<std::string> discovered_endpoint_ids_;
|
||||
};
|
||||
|
||||
// Operator overloads when comparing Ptr<ClientProxy>.
|
||||
bool operator==(const ClientProxy& lhs, const ClientProxy& rhs);
|
||||
bool operator<(const ClientProxy& lhs, const ClientProxy& rhs);
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/client_proxy.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_CLIENT_PROXY_H_
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_v2/listeners.h"
|
||||
#include "core_v2/options.h"
|
||||
#include "core_v2/strategy.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#include "core/strategy.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
@@ -2,324 +2,269 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "platform/base64_utils.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/cancelable_alarm.h"
|
||||
#include "platform/exception.h"
|
||||
#include "platform/logging.h"
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
#include "platform/public/cancelable_alarm.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "securegcm/ukey2_handshake.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::int64_t kTimeoutMillis = 15 * 1000; // 15 seconds
|
||||
std::int32_t kMaxUkey2VerificationStringLength = 32;
|
||||
std::int32_t kTokenLength = 5;
|
||||
securegcm::UKey2Handshake::HandshakeCipher kCipher =
|
||||
securegcm::UKey2Handshake::HandshakeCipher::P256_SHA512;
|
||||
|
||||
} // namespace
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr absl::Duration kTimeout = absl::Seconds(15);
|
||||
constexpr std::int32_t kMaxUkey2VerificationStringLength = 32;
|
||||
constexpr std::int32_t kTokenLength = 5;
|
||||
constexpr securegcm::UKey2Handshake::HandshakeCipher kCipher =
|
||||
securegcm::UKey2Handshake::HandshakeCipher::P256_SHA512;
|
||||
|
||||
// Transforms a raw UKEY2 token (which is a random ByteArray that's
|
||||
// kMaxUkey2VerificationStringLength long) into a kTokenLength string that only
|
||||
// uses A-Z0-9 for each character.
|
||||
string toHumanReadableString(ConstPtr<ByteArray> token) {
|
||||
string result = Base64Utils::encode(token).substr(0, kTokenLength);
|
||||
// uses [A-Z], [0-9], '_', '-' for each character.
|
||||
std::string ToHumanReadableString(const ByteArray& token) {
|
||||
std::string result = Base64Utils::Encode(token).substr(0, kTokenLength);
|
||||
absl::AsciiStrToUpper(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool handleEncryptionSuccess(
|
||||
const string& endpoint_id, Ptr<securegcm::UKey2Handshake> ukey2_handshake,
|
||||
Ptr<typename EncryptionRunner<Platform>::ResultListener> result_listener) {
|
||||
ScopedPtr<Ptr<securegcm::UKey2Handshake>> scoped_ukey2_handshake(
|
||||
ukey2_handshake);
|
||||
|
||||
std::unique_ptr<string> verification_string =
|
||||
scoped_ukey2_handshake->GetVerificationString(
|
||||
kMaxUkey2VerificationStringLength);
|
||||
bool HandleEncryptionSuccess(const std::string& endpoint_id,
|
||||
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
||||
const EncryptionRunner::ResultListener& listener) {
|
||||
std::unique_ptr<std::string> verification_string =
|
||||
ukey2->GetVerificationString(kMaxUkey2VerificationStringLength);
|
||||
if (verification_string == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> raw_authentication_token(MakeConstPtr(
|
||||
new ByteArray(verification_string->data(), verification_string->size())));
|
||||
ByteArray raw_authentication_token(*verification_string);
|
||||
|
||||
result_listener->onEncryptionSuccess(
|
||||
endpoint_id, scoped_ukey2_handshake.release(),
|
||||
toHumanReadableString(raw_authentication_token.get()),
|
||||
raw_authentication_token.release());
|
||||
listener.on_success_cb(endpoint_id, std::move(ukey2),
|
||||
ToHumanReadableString(raw_authentication_token),
|
||||
raw_authentication_token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class CancelableAlarmRunnable : public Runnable {
|
||||
void CancelableAlarmRunnable(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
EndpointChannel* endpoint_channel) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Timing out encryption for client %" PRId64
|
||||
" to endpoint %s after %" PRId64 " ms",
|
||||
client->GetClientId(), endpoint_id.c_str(),
|
||||
static_cast<std::int64_t>(absl::ToInt64Milliseconds(kTimeout)));
|
||||
endpoint_channel->Close();
|
||||
}
|
||||
|
||||
class ServerRunnable final {
|
||||
public:
|
||||
CancelableAlarmRunnable(Ptr<ClientProxy<Platform>> client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel)
|
||||
: client_proxy_(client_proxy),
|
||||
endpoint_id_(endpoint_id),
|
||||
endpoint_channel_(endpoint_channel) {}
|
||||
|
||||
void run() override {
|
||||
NEARBY_LOG(INFO,
|
||||
"Timing out encryption for client %" PRId64
|
||||
" to endpoint %s after %" PRId64 " ms",
|
||||
client_proxy_->getClientId(), endpoint_id_.c_str(),
|
||||
kTimeoutMillis);
|
||||
endpoint_channel_->close();
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<ClientProxy<Platform>> client_proxy_;
|
||||
const string endpoint_id_;
|
||||
Ptr<EndpointChannel> endpoint_channel_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
class ServerRunnable : public Runnable {
|
||||
public:
|
||||
ServerRunnable(Ptr<ClientProxy<Platform>> client_proxy,
|
||||
Ptr<typename Platform::ScheduledExecutorType> alarm_executor,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<typename EncryptionRunner<Platform>::ResultListener>
|
||||
encryption_result_listener)
|
||||
: client_proxy_(client_proxy),
|
||||
ServerRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor,
|
||||
const std::string& endpoint_id, EndpointChannel* channel,
|
||||
EncryptionRunner::ResultListener&& listener)
|
||||
: client_(client),
|
||||
alarm_executor_(alarm_executor),
|
||||
endpoint_id_(endpoint_id),
|
||||
endpoint_channel_(endpoint_channel),
|
||||
encryption_result_listener_(encryption_result_listener) {}
|
||||
channel_(channel),
|
||||
listener_(std::move(listener)) {}
|
||||
|
||||
void run() override {
|
||||
void operator()() const {
|
||||
CancelableAlarm timeout_alarm(
|
||||
"EncryptionRunner.startServer() timeout",
|
||||
MakePtr(new CancelableAlarmRunnable<Platform>(
|
||||
client_proxy_, endpoint_id_, endpoint_channel_)),
|
||||
kTimeoutMillis, alarm_executor_);
|
||||
"EncryptionRunner.StartServer() timeout",
|
||||
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
|
||||
kTimeout, alarm_executor_);
|
||||
|
||||
std::unique_ptr<securegcm::UKey2Handshake> server =
|
||||
securegcm::UKey2Handshake::ForResponder(kCipher);
|
||||
// Java code throws a HandshakeException.
|
||||
if (server == nullptr) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
// Message 1 (Client Init)
|
||||
ExceptionOr<ConstPtr<ByteArray>> client_init = endpoint_channel_->read();
|
||||
ExceptionOr<ByteArray> client_init = channel_->Read();
|
||||
if (!client_init.ok()) {
|
||||
if (Exception::IO == client_init.exception()) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_client_init(client_init.result());
|
||||
|
||||
securegcm::UKey2Handshake::ParseResult parse_result =
|
||||
server->ParseHandshakeMessage(
|
||||
string(scoped_client_init->getData(), scoped_client_init->size()));
|
||||
server->ParseHandshakeMessage(std::string(client_init.result()));
|
||||
|
||||
// Java code throws a HandshakeException / AlertException.
|
||||
if (!parse_result.success) {
|
||||
logException();
|
||||
LogException();
|
||||
if (parse_result.alert_to_send != nullptr) {
|
||||
handleAlertException(parse_result);
|
||||
HandleAlertException(parse_result);
|
||||
}
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 1 from endpoint %s",
|
||||
NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 1 from endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
|
||||
// Message 2 (Server Init)
|
||||
std::unique_ptr<string> server_init = server->GetNextHandshakeMessage();
|
||||
std::unique_ptr<std::string> server_init =
|
||||
server->GetNextHandshakeMessage();
|
||||
|
||||
// Java code throws a HandshakeException.
|
||||
if (server_init == nullptr) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
Exception::Value write_exception = endpoint_channel_->write(
|
||||
MakeConstPtr(new ByteArray(server_init->data(), server_init->size())));
|
||||
if (Exception::NONE != write_exception) {
|
||||
if (Exception::IO == write_exception) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
Exception write_exception =
|
||||
channel_->Write(ByteArray(std::move(*server_init)));
|
||||
if (!write_exception.Ok()) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In startServer(), wrote UKEY2 Message 2 to endpoint %s",
|
||||
NEARBY_LOG(INFO, "In StartServer(), wrote UKEY2 Message 2 to endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
|
||||
// Message 3 (Client Finish)
|
||||
ExceptionOr<ConstPtr<ByteArray>> client_finish = endpoint_channel_->read();
|
||||
ExceptionOr<ByteArray> client_finish = channel_->Read();
|
||||
|
||||
if (!client_finish.ok()) {
|
||||
if (Exception::IO == client_finish.exception()) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_client_finish(client_finish.result());
|
||||
parse_result = server->ParseHandshakeMessage(
|
||||
string(scoped_client_finish->getData(), scoped_client_finish->size()));
|
||||
|
||||
// Java code throws an AlertException or a HandshakeException.
|
||||
if (!parse_result.success) {
|
||||
logException();
|
||||
if (parse_result.alert_to_send != nullptr) {
|
||||
handleAlertException(parse_result);
|
||||
}
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 3 from endpoint %s",
|
||||
parse_result =
|
||||
server->ParseHandshakeMessage(std::string(client_finish.result()));
|
||||
|
||||
// Java code throws an AlertException or a HandshakeException.
|
||||
if (!parse_result.success) {
|
||||
LogException();
|
||||
if (parse_result.alert_to_send != nullptr) {
|
||||
HandleAlertException(parse_result);
|
||||
}
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 3 from endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
|
||||
timeout_alarm.cancel();
|
||||
timeout_alarm.Cancel();
|
||||
|
||||
if (!handleEncryptionSuccess<Platform>(endpoint_id_,
|
||||
MakePtr(server.release()),
|
||||
encryption_result_listener_.get())) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
if (!HandleEncryptionSuccess(endpoint_id_, std::move(server), listener_)) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void logException() {
|
||||
NEARBY_LOG(ERROR, "In startServer(), UKEY2 failed with endpoint %s",
|
||||
void LogException() const {
|
||||
NEARBY_LOG(ERROR, "In StartServer(), UKEY2 failed with endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
}
|
||||
|
||||
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
|
||||
timeout_alarm->cancel();
|
||||
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
|
||||
endpoint_channel_);
|
||||
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
|
||||
timeout_alarm->Cancel();
|
||||
listener_.on_failure_cb(endpoint_id_, channel_);
|
||||
}
|
||||
|
||||
void handleAlertException(
|
||||
const securegcm::UKey2Handshake::ParseResult& parse_result) {
|
||||
Exception::Value write_exception = endpoint_channel_->write(
|
||||
MakeConstPtr(new ByteArray(parse_result.alert_to_send->data(),
|
||||
parse_result.alert_to_send->size())));
|
||||
if (Exception::NONE != write_exception) {
|
||||
if (Exception::IO == write_exception) {
|
||||
NEARBY_LOG(WARNING,
|
||||
"In startServer(), client %" PRId64
|
||||
" failed to pass the alert error message to endpoint %s",
|
||||
client_proxy_->getClientId(), endpoint_id_.c_str());
|
||||
}
|
||||
void HandleAlertException(
|
||||
const securegcm::UKey2Handshake::ParseResult& parse_result) const {
|
||||
Exception write_exception =
|
||||
channel_->Write(ByteArray(*parse_result.alert_to_send));
|
||||
if (!write_exception.Ok()) {
|
||||
NEARBY_LOG(WARNING,
|
||||
"In StartServer(), client %" PRId64
|
||||
" failed to pass the alert error message to endpoint %s",
|
||||
client_->GetClientId(), endpoint_id_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<ClientProxy<Platform>> client_proxy_;
|
||||
Ptr<typename Platform::ScheduledExecutorType> alarm_executor_;
|
||||
const string endpoint_id_;
|
||||
Ptr<EndpointChannel> endpoint_channel_;
|
||||
ScopedPtr<Ptr<typename EncryptionRunner<Platform>::ResultListener>>
|
||||
encryption_result_listener_;
|
||||
ClientProxy* client_;
|
||||
ScheduledExecutor* alarm_executor_;
|
||||
const std::string endpoint_id_;
|
||||
EndpointChannel* channel_;
|
||||
EncryptionRunner::ResultListener listener_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
class ClientRunnable : public Runnable {
|
||||
class ClientRunnable final {
|
||||
public:
|
||||
ClientRunnable(Ptr<ClientProxy<Platform>> client_proxy,
|
||||
Ptr<typename Platform::ScheduledExecutorType> alarm_executor,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<typename EncryptionRunner<Platform>::ResultListener>
|
||||
encryption_result_listener)
|
||||
: client_proxy_(client_proxy),
|
||||
ClientRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor,
|
||||
const std::string& endpoint_id, EndpointChannel* channel,
|
||||
EncryptionRunner::ResultListener&& listener)
|
||||
: client_(client),
|
||||
alarm_executor_(alarm_executor),
|
||||
endpoint_id_(endpoint_id),
|
||||
endpoint_channel_(endpoint_channel),
|
||||
encryption_result_listener_(encryption_result_listener) {}
|
||||
channel_(channel),
|
||||
listener_(std::move(listener)) {}
|
||||
|
||||
void run() override {
|
||||
void operator()() const {
|
||||
CancelableAlarm timeout_alarm(
|
||||
"EncryptionRunner.startClient() timeout",
|
||||
MakePtr(new CancelableAlarmRunnable<Platform>(
|
||||
client_proxy_, endpoint_id_, endpoint_channel_)),
|
||||
kTimeoutMillis, alarm_executor_);
|
||||
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
|
||||
kTimeout, alarm_executor_);
|
||||
|
||||
std::unique_ptr<securegcm::UKey2Handshake> client =
|
||||
std::unique_ptr<securegcm::UKey2Handshake> crypto =
|
||||
securegcm::UKey2Handshake::ForInitiator(kCipher);
|
||||
|
||||
// Java code throws a HandshakeException.
|
||||
if (client == nullptr) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
if (crypto == nullptr) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
// Message 1 (Client Init)
|
||||
std::unique_ptr<string> client_init = client->GetNextHandshakeMessage();
|
||||
std::unique_ptr<std::string> client_init =
|
||||
crypto->GetNextHandshakeMessage();
|
||||
|
||||
// Java code throws a HandshakeException.
|
||||
if (client_init == nullptr) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
Exception::Value write_init_exception = endpoint_channel_->write(
|
||||
MakeConstPtr(new ByteArray(client_init->data(), client_init->size())));
|
||||
if (Exception::NONE != write_init_exception) {
|
||||
if (Exception::IO == write_init_exception) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
Exception write_init_exception = channel_->Write(ByteArray(*client_init));
|
||||
if (!write_init_exception.Ok()) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In startClient(), wrote UKEY2 Message 1 to endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
|
||||
// Message 2 (Server Init)
|
||||
ExceptionOr<ConstPtr<ByteArray>> server_init = endpoint_channel_->read();
|
||||
ExceptionOr<ByteArray> server_init = channel_->Read();
|
||||
|
||||
if (!server_init.ok()) {
|
||||
if (Exception::IO == server_init.exception()) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_server_init(server_init.result());
|
||||
securegcm::UKey2Handshake::ParseResult parse_result =
|
||||
client->ParseHandshakeMessage(
|
||||
string(scoped_server_init->getData(), scoped_server_init->size()));
|
||||
crypto->ParseHandshakeMessage(std::string(server_init.result()));
|
||||
|
||||
// Java code throws an AlertException or a HandshakeException.
|
||||
if (!parse_result.success) {
|
||||
logException();
|
||||
LogException();
|
||||
if (parse_result.alert_to_send != nullptr) {
|
||||
handleAlertException(parse_result);
|
||||
HandleAlertException(parse_result);
|
||||
}
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -327,109 +272,95 @@ class ClientRunnable : public Runnable {
|
||||
endpoint_id_.c_str());
|
||||
|
||||
// Message 3 (Client Finish)
|
||||
std::unique_ptr<string> client_finish = client->GetNextHandshakeMessage();
|
||||
std::unique_ptr<std::string> client_finish =
|
||||
crypto->GetNextHandshakeMessage();
|
||||
|
||||
// Java code throws a HandshakeException.
|
||||
if (client_finish == nullptr) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
Exception::Value write_finish_exception =
|
||||
endpoint_channel_->write(MakeConstPtr(
|
||||
new ByteArray(client_finish->data(), client_finish->size())));
|
||||
if (Exception::NONE != write_finish_exception) {
|
||||
if (Exception::IO == write_finish_exception) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
Exception write_finish_exception =
|
||||
channel_->Write(ByteArray(*client_finish));
|
||||
if (!write_finish_exception.Ok()) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "In startClient(), wrote UKEY2 Message 3 to endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
|
||||
timeout_alarm.cancel();
|
||||
timeout_alarm.Cancel();
|
||||
|
||||
if (!handleEncryptionSuccess<Platform>(endpoint_id_,
|
||||
MakePtr(client.release()),
|
||||
encryption_result_listener_.get())) {
|
||||
logException();
|
||||
handleHandshakeOrIOException(&timeout_alarm);
|
||||
if (!HandleEncryptionSuccess(endpoint_id_, std::move(crypto), listener_)) {
|
||||
LogException();
|
||||
HandleHandshakeOrIoException(&timeout_alarm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void logException() {
|
||||
void LogException() const {
|
||||
NEARBY_LOG(ERROR, "In startClient(), UKEY2 failed with endpoint %s",
|
||||
endpoint_id_.c_str());
|
||||
}
|
||||
|
||||
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
|
||||
timeout_alarm->cancel();
|
||||
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
|
||||
endpoint_channel_);
|
||||
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
|
||||
timeout_alarm->Cancel();
|
||||
listener_.on_failure_cb(endpoint_id_, channel_);
|
||||
}
|
||||
|
||||
void handleAlertException(
|
||||
const securegcm::UKey2Handshake::ParseResult& parse_result) {
|
||||
Exception::Value write_exception = endpoint_channel_->write(
|
||||
MakeConstPtr(new ByteArray(parse_result.alert_to_send->data(),
|
||||
parse_result.alert_to_send->size())));
|
||||
if (Exception::NONE != write_exception) {
|
||||
if (Exception::IO == write_exception) {
|
||||
NEARBY_LOG(WARNING,
|
||||
"In startClient(), client %" PRId64
|
||||
" failed to pass the alert error message to endpoint %s",
|
||||
client_proxy_->getClientId(), endpoint_id_.c_str());
|
||||
}
|
||||
void HandleAlertException(
|
||||
const securegcm::UKey2Handshake::ParseResult& parse_result) const {
|
||||
Exception write_exception =
|
||||
channel_->Write(ByteArray(*parse_result.alert_to_send));
|
||||
if (!write_exception.Ok()) {
|
||||
NEARBY_LOG(WARNING,
|
||||
"In startClient(), client %" PRId64
|
||||
" failed to pass the alert error message to endpoint %s",
|
||||
client_->GetClientId(), endpoint_id_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<ClientProxy<Platform>> client_proxy_;
|
||||
Ptr<typename Platform::ScheduledExecutorType> alarm_executor_;
|
||||
const string endpoint_id_;
|
||||
Ptr<EndpointChannel> endpoint_channel_;
|
||||
ScopedPtr<Ptr<typename EncryptionRunner<Platform>::ResultListener>>
|
||||
encryption_result_listener_;
|
||||
ClientProxy* client_;
|
||||
ScheduledExecutor* alarm_executor_;
|
||||
const std::string endpoint_id_;
|
||||
EndpointChannel* channel_;
|
||||
EncryptionRunner::ResultListener listener_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename Platform>
|
||||
EncryptionRunner<Platform>::EncryptionRunner()
|
||||
: alarm_executor_(Platform::createScheduledExecutor()),
|
||||
server_executor_(Platform::createSingleThreadExecutor()),
|
||||
client_executor_(Platform::createSingleThreadExecutor()) {}
|
||||
|
||||
template <typename Platform>
|
||||
EncryptionRunner<Platform>::~EncryptionRunner() {
|
||||
EncryptionRunner::~EncryptionRunner() {
|
||||
// Stop all the ongoing Runnables (as gracefully as possible).
|
||||
client_executor_->shutdown();
|
||||
server_executor_->shutdown();
|
||||
alarm_executor_->shutdown();
|
||||
client_executor_.Shutdown();
|
||||
server_executor_.Shutdown();
|
||||
alarm_executor_.Shutdown();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void EncryptionRunner<Platform>::startServer(
|
||||
Ptr<ClientProxy<Platform>> client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<ResultListener> result_listener) {
|
||||
server_executor_->execute(MakePtr(new ServerRunnable<Platform>(
|
||||
client_proxy, alarm_executor_.get(), endpoint_id, endpoint_channel,
|
||||
result_listener)));
|
||||
void EncryptionRunner::StartServer(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
EndpointChannel* endpoint_channel,
|
||||
EncryptionRunner::ResultListener&& listener) {
|
||||
server_executor_.Execute(
|
||||
[runnable{ServerRunnable(client, &alarm_executor_, endpoint_id,
|
||||
endpoint_channel, std::move(listener))}]() {
|
||||
runnable();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void EncryptionRunner<Platform>::startClient(
|
||||
Ptr<ClientProxy<Platform>> client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<ResultListener> result_listener) {
|
||||
client_executor_->execute(MakePtr(new ClientRunnable<Platform>(
|
||||
client_proxy, alarm_executor_.get(), endpoint_id, endpoint_channel,
|
||||
result_listener)));
|
||||
void EncryptionRunner::StartClient(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
EndpointChannel* endpoint_channel,
|
||||
EncryptionRunner::ResultListener&& listener) {
|
||||
client_executor_.Execute(
|
||||
[runnable{ClientRunnable(client, &alarm_executor_, endpoint_id,
|
||||
endpoint_channel, std::move(listener))}]() {
|
||||
runnable();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#ifndef CORE_INTERNAL_ENCRYPTION_RUNNER_H_
|
||||
#define CORE_INTERNAL_ENCRYPTION_RUNNER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "core/listeners.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/scheduled_executor.h"
|
||||
#include "platform/public/single_thread_executor.h"
|
||||
#include "securegcm/ukey2_handshake.h"
|
||||
|
||||
namespace location {
|
||||
@@ -14,60 +17,56 @@ namespace connections {
|
||||
|
||||
// Encrypts a connection over UKEY2.
|
||||
//
|
||||
// <p>NOTE: Stalled EndpointChannels will be disconnected after {TIMEOUT_MILLIS}
|
||||
// milliseconds. This is to prevent unverified endpoints from maintaining an
|
||||
// NOTE: Stalled EndpointChannels will be disconnected after kTimeout.
|
||||
// This is to prevent unverified endpoints from maintaining an
|
||||
// indefinite connection to us.
|
||||
template <typename Platform>
|
||||
class EncryptionRunner {
|
||||
public:
|
||||
EncryptionRunner();
|
||||
EncryptionRunner() = default;
|
||||
~EncryptionRunner();
|
||||
|
||||
class ResultListener {
|
||||
public:
|
||||
virtual ~ResultListener() {}
|
||||
|
||||
struct ResultListener {
|
||||
// @EncryptionRunnerThread
|
||||
virtual void onEncryptionSuccess(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::UKey2Handshake> ukey2_handshake,
|
||||
const string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token) = 0;
|
||||
std::function<void(const std::string& endpoint_id,
|
||||
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
||||
const std::string& auth_token,
|
||||
const ByteArray& raw_auth_token)>
|
||||
on_success_cb =
|
||||
DefaultCallback<const std::string&,
|
||||
std::unique_ptr<securegcm::UKey2Handshake>,
|
||||
const std::string&, const ByteArray&>();
|
||||
|
||||
// Encryption has failed. The remote_endpoint_id and channel are given so
|
||||
// that any pending state can be cleaned up.
|
||||
//
|
||||
// <p>We return the EndpointChannel because, at this stage, simultaneous
|
||||
// We return the EndpointChannel because, at this stage, simultaneous
|
||||
// connections are a possibility. Use this channel to verify that the state
|
||||
// you're cleaning up is for this EndpointChannel, and not state for another
|
||||
// channel to the same endpoint.
|
||||
//
|
||||
// @EncryptionRunnerThread
|
||||
virtual void onEncryptionFailure(const string& endpoint_id,
|
||||
Ptr<EndpointChannel> channel) = 0;
|
||||
std::function<void(const std::string& endpoint_id,
|
||||
EndpointChannel* channel)>
|
||||
on_failure_cb = DefaultCallback<const std::string&, EndpointChannel*>();
|
||||
};
|
||||
|
||||
// @AnyThread
|
||||
void startServer(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<ResultListener> result_listener);
|
||||
void StartServer(ClientProxy* client, const std::string& endpoint_id,
|
||||
EndpointChannel* endpoint_channel,
|
||||
ResultListener&& result_listener);
|
||||
// @AnyThread
|
||||
void startClient(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<ResultListener> result_listener);
|
||||
void StartClient(ClientProxy* client, const std::string& endpoint_id,
|
||||
EndpointChannel* endpoint_channel,
|
||||
ResultListener&& result_listener);
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<typename Platform::ScheduledExecutorType> > alarm_executor_;
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType> > server_executor_;
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType> > client_executor_;
|
||||
ScheduledExecutor alarm_executor_;
|
||||
SingleThreadExecutor server_executor_;
|
||||
SingleThreadExecutor client_executor_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/encryption_runner.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_ENCRYPTION_RUNNER_H_
|
||||
|
||||
+7
-7
@@ -1,11 +1,11 @@
|
||||
#include "core_v2/internal/encryption_runner.h"
|
||||
#include "core/internal/encryption_runner.h"
|
||||
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_channel.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/pipe.h"
|
||||
#include "platform_v2/public/system_clock.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@@ -2,13 +2,14 @@
|
||||
#define CORE_INTERNAL_ENDPOINT_CHANNEL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/exception.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "securegcm/d2d_connection_context_v1.h"
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -16,52 +17,58 @@ namespace connections {
|
||||
|
||||
class EndpointChannel {
|
||||
public:
|
||||
virtual ~EndpointChannel() {}
|
||||
virtual ~EndpointChannel() = default;
|
||||
|
||||
virtual ExceptionOr<ConstPtr<ByteArray> >
|
||||
read() = 0; // throws Exception::IO, Exception::INTERRUPTED
|
||||
using EncryptionContext = ::securegcm::D2DConnectionContextV1;
|
||||
|
||||
virtual Exception::Value write(
|
||||
ConstPtr<ByteArray> data) = 0; // throws Exception::IO
|
||||
virtual ExceptionOr<ByteArray>
|
||||
Read() = 0; // throws Exception::IO, Exception::INTERRUPTED
|
||||
|
||||
virtual Exception Write(const ByteArray& data) = 0; // throws Exception::IO
|
||||
|
||||
// Closes this EndpointChannel, without tracking the closure in analytics.
|
||||
virtual void close() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
// Closes this EndpointChannel and records the closure with the given reason.
|
||||
virtual void close(proto::connections::DisconnectionReason reason) = 0;
|
||||
virtual void Close(proto::connections::DisconnectionReason reason) = 0;
|
||||
|
||||
// Returns a one-word type descriptor for the concrete EndpointChannel
|
||||
// implementation that can be used in log messages; eg: BLUETOOTH, BLE, WIFI.
|
||||
virtual string getType() = 0;
|
||||
virtual std::string GetType() const = 0;
|
||||
|
||||
// Returns the name of the EndpointChannel.
|
||||
virtual string getName() = 0;
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
// Returns the analytics enum representing the medium of this EndpointChannel.
|
||||
virtual proto::connections::Medium getMedium() = 0;
|
||||
virtual proto::connections::Medium GetMedium() const = 0;
|
||||
|
||||
// Enables encryption on the EndpointChannel.
|
||||
//
|
||||
// This method takes ownership of the passed-in 'connection_context'.
|
||||
virtual void enableEncryption(
|
||||
Ptr<securegcm::D2DConnectionContextV1> connection_context) = 0;
|
||||
virtual void EnableEncryption(std::shared_ptr<EncryptionContext> context) = 0;
|
||||
|
||||
// True if the EndpointChannel is currently pausing all writes.
|
||||
virtual bool isPaused() = 0;
|
||||
virtual bool IsPaused() const = 0;
|
||||
|
||||
// Pauses all writes on this EndpointChannel until resume() is called.
|
||||
virtual void pause() = 0;
|
||||
virtual void Pause() = 0;
|
||||
|
||||
// Resumes any writes on this EndpointChannel that were suspended when pause()
|
||||
// was called.
|
||||
virtual void resume() = 0;
|
||||
virtual void Resume() = 0;
|
||||
|
||||
// Returns the timestamp of the last read from this endpoint, or -1 if no
|
||||
// reads have occurred.
|
||||
// TODO(tracyzhou): Clarify units of timestamp.
|
||||
virtual std::int64_t getLastReadTimestamp() = 0;
|
||||
virtual absl::Time GetLastReadTimestamp() const = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(const EndpointChannel& lhs, const EndpointChannel& rhs) {
|
||||
return (lhs.GetType() == rhs.GetType()) && (lhs.GetName() == rhs.GetName()) &&
|
||||
(lhs.GetMedium() == rhs.GetMedium());
|
||||
}
|
||||
|
||||
inline bool operator!=(const EndpointChannel& lhs, const EndpointChannel& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@@ -1,284 +1,138 @@
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
|
||||
#include "core/internal/ble_endpoint_channel.h"
|
||||
#include "core/internal/bluetooth_endpoint_channel.h"
|
||||
#include "core/internal/wifi_lan_endpoint_channel.h"
|
||||
#include "platform/synchronized.h"
|
||||
#include <memory>
|
||||
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
EndpointChannelManager::EndpointChannelManager(
|
||||
Ptr<MediumManager<Platform> > medium_manager)
|
||||
: lock_(Platform::createLock()),
|
||||
medium_manager_(medium_manager),
|
||||
channel_state_(new ChannelState()) {}
|
||||
|
||||
EndpointChannelManager::~EndpointChannelManager() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// TODO(tracyzhou): logger.atDebug().log("Initiating shutdown of
|
||||
// EndpointChannelManager.")
|
||||
channel_state_.destroy();
|
||||
// TODO(tracyzhou): logger.atDebug().log("EndpointChannelManager has shut
|
||||
// down.");
|
||||
MutexLock lock(&mutex_);
|
||||
channel_state_.DestroyAll();
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::createOutgoingBluetoothEndpointChannel(
|
||||
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket) {
|
||||
return BluetoothEndpointChannel::createOutgoing(medium_manager_, channel_name,
|
||||
bluetooth_socket);
|
||||
void EndpointChannelManager::RegisterChannelForEndpoint(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
SetActiveEndpointChannel(client, endpoint_id, std::move(channel));
|
||||
|
||||
NEARBY_LOG(INFO, "Registered channel: id=%s", endpoint_id.c_str());
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::createIncomingBluetoothEndpointChannel(
|
||||
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket) {
|
||||
return BluetoothEndpointChannel::createIncoming(medium_manager_, channel_name,
|
||||
bluetooth_socket);
|
||||
}
|
||||
void EndpointChannelManager::ReplaceChannelForEndpoint(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
Ptr<EndpointChannel> EndpointChannelManager::createOutgoingBLEEndpointChannel(
|
||||
const string& channel_name, Ptr<BLESocket> ble_socket) {
|
||||
return BLEEndpointChannel::createOutgoing(medium_manager_, channel_name,
|
||||
ble_socket);
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel> EndpointChannelManager::createIncomingBLEEndpointChannel(
|
||||
const string& channel_name, Ptr<BLESocket> ble_socket) {
|
||||
return BLEEndpointChannel::createIncoming(medium_manager_, channel_name,
|
||||
ble_socket);
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::CreateOutgoingWifiLanEndpointChannel(
|
||||
const string& channel_name, Ptr<WifiLanSocket> wifi_lan_socket) {
|
||||
return WifiLanEndpointChannel::CreateOutgoing(
|
||||
medium_manager_, channel_name, wifi_lan_socket);
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::CreateIncomingWifiLanEndpointChannel(
|
||||
const string& channel_name, Ptr<WifiLanSocket> wifi_lan_socket) {
|
||||
return WifiLanEndpointChannel::CreateIncoming(
|
||||
medium_manager_, channel_name, wifi_lan_socket);
|
||||
}
|
||||
|
||||
void EndpointChannelManager::registerChannelForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Just in case there was a previous channel, unregister (and, thus, close) it
|
||||
// now.
|
||||
unregisterChannelForEndpoint(endpoint_id);
|
||||
|
||||
setActiveEndpointChannel(client_proxy, endpoint_id, endpoint_channel);
|
||||
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
#ifdef BANDWIDTH_UPGRADE_MANAGER_IMPLEMENTED
|
||||
Ptr<EndpointChannel> EndpointChannelManager::replaceChannelForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
ScopedPtr<Ptr<EndpointChannel> > scoped_previous_endpoint_channel(
|
||||
channel_state_->getChannelForEndpoint(endpoint_id));
|
||||
if (scoped_previous_endpoint_channel.isNull()) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return Ptr<EndpointChannel>();
|
||||
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
|
||||
if (endpoint != nullptr && endpoint->channel == nullptr) {
|
||||
NEARBY_LOG(INFO, "Channel is missing while trying to update: id=%s",
|
||||
endpoint_id.c_str());
|
||||
}
|
||||
|
||||
setActiveEndpointChannel(client_proxy, endpoint_id, endpoint_channel);
|
||||
|
||||
// TODO(tracyzhou): Add logging.
|
||||
|
||||
return scoped_previous_endpoint_channel.release();
|
||||
SetActiveEndpointChannel(client, endpoint_id, std::move(channel));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool EndpointChannelManager::encryptChannelForEndpoint(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
|
||||
Synchronized s(lock_.get());
|
||||
bool EndpointChannelManager::EncryptChannelForEndpoint(
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EncryptionContext> context) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
ScopedPtr<Ptr<EndpointChannel> > scoped_endpoint_channel(
|
||||
channel_state_->getChannelForEndpoint(endpoint_id));
|
||||
if (scoped_endpoint_channel.isNull()) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
return false;
|
||||
channel_state_.UpdateEncryptionContextForEndpoint(endpoint_id,
|
||||
std::move(context));
|
||||
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
|
||||
return channel_state_.EncryptChannel(endpoint);
|
||||
}
|
||||
|
||||
std::shared_ptr<EndpointChannel> EndpointChannelManager::GetChannelForEndpoint(
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
|
||||
if (endpoint == nullptr) {
|
||||
NEARBY_LOG(INFO, "No channel info: id=%s", endpoint_id.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
// We found the requested EndpointChannel, so encrypt it.
|
||||
encryptChannel(endpoint_id, scoped_endpoint_channel.get(),
|
||||
encryption_context);
|
||||
|
||||
// Then update 'endpoint_id' to use this new 'encryption_context' here
|
||||
// onwards.
|
||||
//
|
||||
// Remember to manage the memory of the returned
|
||||
// Ptr<securegcm::D2DConnectionContextV1> responsibly, even though we don't
|
||||
// need what's returned.
|
||||
ScopedPtr<Ptr<securegcm::D2DConnectionContextV1> >(
|
||||
channel_state_->updateEncryptionContextForEndpoint(endpoint_id,
|
||||
encryption_context));
|
||||
return true;
|
||||
return endpoint->channel;
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel> EndpointChannelManager::getChannelForEndpoint(
|
||||
const string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
void EndpointChannelManager::SetActiveEndpointChannel(
|
||||
ClientProxy* client, const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel) {
|
||||
// Update the channel first, then encrypt this new channel, if
|
||||
// crypto context is present.
|
||||
channel_state_.UpdateChannelForEndpoint(endpoint_id, std::move(channel));
|
||||
|
||||
return channel_state_->getChannelForEndpoint(endpoint_id);
|
||||
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
|
||||
if (endpoint->IsEncrypted()) channel_state_.EncryptChannel(endpoint);
|
||||
}
|
||||
|
||||
void EndpointChannelManager::setActiveEndpointChannel(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel) {
|
||||
#ifdef BANDWIDTH_UPGRADE_MANAGER_IMPLEMENTED
|
||||
// If the endpoint is currently encrypted, encrypt this new
|
||||
// 'endpoint_channel'.
|
||||
if (channel_state_->isEndpointEncrypted(endpoint_id)) {
|
||||
encryptChannel(
|
||||
endpoint_id, endpoint_channel,
|
||||
channel_state_->getEncryptionContextForEndpoint(endpoint_id));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Then update 'endpoint_id' to use this new 'endpoint_channel' here onwards.
|
||||
//
|
||||
// Remember to manage the memory of the returned Ptr<EndpointChannel>
|
||||
// responsibly, even though we don't need what's returned.
|
||||
ScopedPtr<Ptr<EndpointChannel> >(
|
||||
channel_state_->updateChannelForEndpoint(endpoint_id, endpoint_channel));
|
||||
}
|
||||
|
||||
void EndpointChannelManager::encryptChannel(
|
||||
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
endpoint_channel->enableEncryption(encryption_context);
|
||||
int EndpointChannelManager::GetConnectedEndpointsCount() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return channel_state_.GetConnectedEndpointsCount();
|
||||
}
|
||||
|
||||
///////////////////////////////// ChannelState /////////////////////////////////
|
||||
|
||||
EndpointChannelManager::ChannelState::~ChannelState() {
|
||||
while (!endpoint_id_to_metadata_.empty()) {
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.begin();
|
||||
// TODO(tracyzhou): Add logging.
|
||||
removeEndpoint(it->first,
|
||||
proto::connections::DisconnectionReason::SHUTDOWN);
|
||||
// endpoint - channel endpoint to encrypt
|
||||
bool EndpointChannelManager::ChannelState::EncryptChannel(
|
||||
EndpointChannelManager::ChannelState::EndpointData* endpoint) {
|
||||
if (endpoint != nullptr && endpoint->channel != nullptr &&
|
||||
endpoint->context != nullptr) {
|
||||
endpoint->channel->EnableEncryption(endpoint->context);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EndpointChannelManager::ChannelState::isEndpointEncrypted(
|
||||
const string& endpoint_id) {
|
||||
return !getEncryptionContextForEndpoint(endpoint_id).isNull();
|
||||
EndpointChannelManager::ChannelState::EndpointData*
|
||||
EndpointChannelManager::ChannelState::LookupEndpointData(
|
||||
const std::string& endpoint_id) {
|
||||
auto item = endpoints_.find(endpoint_id);
|
||||
return item != endpoints_.end() ? &item->second : nullptr;
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::ChannelState::updateChannelForEndpoint(
|
||||
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel) {
|
||||
Ptr<EndpointChannel> previous_endpoint_channel;
|
||||
Ptr<EndpointMetaData> endpoint_metadata;
|
||||
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.find(endpoint_id);
|
||||
if (it == endpoint_id_to_metadata_.end()) {
|
||||
endpoint_metadata = MakePtr(new EndpointMetaData());
|
||||
} else {
|
||||
endpoint_metadata = it->second;
|
||||
previous_endpoint_channel = endpoint_metadata->endpoint_channel;
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<EndpointChannel> > scoped_previous_endpoint_channel(
|
||||
previous_endpoint_channel);
|
||||
|
||||
endpoint_metadata->endpoint_channel = endpoint_channel;
|
||||
endpoint_channel.clear();
|
||||
endpoint_id_to_metadata_[endpoint_id] = endpoint_metadata;
|
||||
|
||||
return scoped_previous_endpoint_channel.release();
|
||||
void EndpointChannelManager::ChannelState::UpdateChannelForEndpoint(
|
||||
const std::string& endpoint_id, std::unique_ptr<EndpointChannel> channel) {
|
||||
// Create EndpointData instance, if necessary, and populate channel.
|
||||
endpoints_[endpoint_id].channel = std::move(channel);
|
||||
}
|
||||
|
||||
Ptr<securegcm::D2DConnectionContextV1>
|
||||
EndpointChannelManager::ChannelState::updateEncryptionContextForEndpoint(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
|
||||
Ptr<securegcm::D2DConnectionContextV1> previous_encryption_context;
|
||||
Ptr<EndpointMetaData> endpoint_metadata;
|
||||
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.find(endpoint_id);
|
||||
if (it == endpoint_id_to_metadata_.end()) {
|
||||
endpoint_metadata = MakePtr(new EndpointMetaData());
|
||||
} else {
|
||||
endpoint_metadata = it->second;
|
||||
previous_encryption_context = endpoint_metadata->encryption_context;
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<securegcm::D2DConnectionContextV1> >
|
||||
scoped_previous_encryption_context(previous_encryption_context);
|
||||
|
||||
endpoint_metadata->encryption_context = encryption_context;
|
||||
endpoint_id_to_metadata_[endpoint_id] = endpoint_metadata;
|
||||
|
||||
return scoped_previous_encryption_context.release();
|
||||
void EndpointChannelManager::ChannelState::UpdateEncryptionContextForEndpoint(
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EncryptionContext> context) {
|
||||
// Create EndpointData instance, if necessary, and populate crypto context.
|
||||
endpoints_[endpoint_id].context = std::move(context);
|
||||
}
|
||||
|
||||
bool EndpointChannelManager::ChannelState::removeEndpoint(
|
||||
const string& endpoint_id, proto::connections::DisconnectionReason reason) {
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.find(endpoint_id);
|
||||
if (it == endpoint_id_to_metadata_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
it->second->endpoint_channel->close(reason);
|
||||
it->second.destroy();
|
||||
endpoint_id_to_metadata_.erase(it);
|
||||
bool EndpointChannelManager::ChannelState::RemoveEndpoint(
|
||||
const std::string& endpoint_id,
|
||||
proto::connections::DisconnectionReason reason) {
|
||||
auto item = endpoints_.find(endpoint_id);
|
||||
if (item == endpoints_.end()) return false;
|
||||
item->second.disconnect_reason = reason;
|
||||
endpoints_.erase(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
Ptr<securegcm::D2DConnectionContextV1>
|
||||
EndpointChannelManager::ChannelState::getEncryptionContextForEndpoint(
|
||||
const string& endpoint_id) {
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.find(endpoint_id);
|
||||
if (it == endpoint_id_to_metadata_.end()) {
|
||||
return Ptr<securegcm::D2DConnectionContextV1>();
|
||||
}
|
||||
bool EndpointChannelManager::UnregisterChannelForEndpoint(
|
||||
const std::string& endpoint_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return it->second->encryption_context;
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel>
|
||||
EndpointChannelManager::ChannelState::getChannelForEndpoint(
|
||||
const string& endpoint_id) {
|
||||
typename EndpointIdToMetadataMap::iterator it =
|
||||
endpoint_id_to_metadata_.find(endpoint_id);
|
||||
if (it == endpoint_id_to_metadata_.end()) {
|
||||
return Ptr<EndpointChannel>();
|
||||
}
|
||||
|
||||
return it->second->endpoint_channel;
|
||||
}
|
||||
|
||||
bool EndpointChannelManager::unregisterChannelForEndpoint(
|
||||
const string& endpoint_id) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!channel_state_->removeEndpoint(
|
||||
if (!channel_state_.RemoveEndpoint(
|
||||
endpoint_id,
|
||||
proto::connections::DisconnectionReason::LOCAL_DISCONNECTION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(tracyzhou): Add logging.
|
||||
NEARBY_LOG(INFO, "Unregistered channel: id=%s", endpoint_id.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,80 +1,86 @@
|
||||
#ifndef CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
|
||||
#define CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "core/internal/medium_manager.h"
|
||||
#include "platform/api/ble.h"
|
||||
#include "platform/api/bluetooth_classic.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/api/platform.h"
|
||||
#include "platform/api/wifi_lan.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "securegcm/d2d_connection_context_v1.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
// NOTE(std::string):
|
||||
// All the strings in internal class public interfaces should be exchanged as
|
||||
// const std::string& if they are immutable, and as std::string
|
||||
// it they are mutable.
|
||||
// This is to keep all the internal classes compatible with each other,
|
||||
// and minimize resources spent on the type conversion.
|
||||
// Project-wide, strings are either passed around as reference (which has
|
||||
// zero maintenance costs, and sizeof(void*) memory usage => passed around in a
|
||||
// CPU register), and whenever lifetime etension is required, it must be copied
|
||||
// to std::string instance (which will again propagate as a const reference
|
||||
// within it's lifetime domain).
|
||||
|
||||
// Manages the communication channels to all the remote endpoints with which we
|
||||
// are interacting, including serving as a factory for creating said channels.
|
||||
//
|
||||
// The factory methods would be static, but for the fact that they need to use
|
||||
// the MediumManager.
|
||||
class EndpointChannelManager {
|
||||
// are interacting.
|
||||
class EndpointChannelManager final {
|
||||
public:
|
||||
using Platform = platform::ImplementationPlatform;
|
||||
using EncryptionContext = EndpointChannel::EncryptionContext;
|
||||
|
||||
explicit EndpointChannelManager(Ptr<MediumManager<Platform>> medium_manager);
|
||||
~EndpointChannelManager();
|
||||
|
||||
Ptr<EndpointChannel> createOutgoingBluetoothEndpointChannel(
|
||||
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket);
|
||||
Ptr<EndpointChannel> createIncomingBluetoothEndpointChannel(
|
||||
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket);
|
||||
|
||||
Ptr<EndpointChannel> createOutgoingBLEEndpointChannel(
|
||||
const string& channel_name, Ptr<BLESocket> ble_socket);
|
||||
Ptr<EndpointChannel> createIncomingBLEEndpointChannel(
|
||||
const string& channel_name, Ptr<BLESocket> ble_socket);
|
||||
|
||||
Ptr<EndpointChannel> CreateOutgoingWifiLanEndpointChannel(
|
||||
const string& channel_name, Ptr<WifiLanSocket> wifi_lan_socket);
|
||||
Ptr<EndpointChannel> CreateIncomingWifiLanEndpointChannel(
|
||||
const string& channel_name, Ptr<WifiLanSocket> wifi_lan_socket);
|
||||
|
||||
// Registers the initial EndpointChannel to be associated with an endpoint;
|
||||
// if there already exists a previously-associated EndpointChannel, that will
|
||||
// be closed before continuing the registration.
|
||||
void registerChannelForEndpoint(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel);
|
||||
void RegisterChannelForEndpoint(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
#ifdef BANDWIDTH_UPGRADE_MANAGER_IMPLEMENTED
|
||||
// Replaces the EndpointChannel to be associated with an endpoint from here on
|
||||
// in, transferring the encryption context from the previous EndpointChannel
|
||||
// to the newly-provided EndpointChannel.
|
||||
void ReplaceChannelForEndpoint(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool EncryptChannelForEndpoint(const std::string& endpoint_id,
|
||||
std::unique_ptr<EncryptionContext> context)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// NOTE(shared_ptr<> usage):
|
||||
//
|
||||
// Returns the previous EndpointChannel, or null Ptr object if called out of
|
||||
// order.
|
||||
Ptr<EndpointChannel> replaceChannelForEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel);
|
||||
#endif
|
||||
|
||||
bool encryptChannelForEndpoint(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context);
|
||||
|
||||
// The returned Ptr will be owned (and destroyed) by the caller.
|
||||
Ptr<EndpointChannel> getChannelForEndpoint(const string& endpoint_id);
|
||||
// EndpointChannelManager is holding an EndpointChannel instance;
|
||||
// GetChannelForEndpoint() is passing ownership over to a worker thread.
|
||||
// It is not a pointer passing but an ownership passing, to guarantee that
|
||||
// channel instance will not disappear underneath the feet of a worker thread
|
||||
// inside EndpointManager [ EndpointManager::EndpointChannelLoopRunnable() ].
|
||||
// If it is just a pointer, Channel will get destroyed while in use by a
|
||||
// worker thread. shared_ptr is a simple and reliable tool to avoid that.
|
||||
//
|
||||
// The reason why it can not be std::unique_ptr<> is: there are other code
|
||||
// paths that expect to be able to read the pointer value multiple times, from
|
||||
// multiple places (each of them needs "ownership" for the duration of their
|
||||
// use). EndpointManager::SendTransferFrameBytes() is another such place.
|
||||
// If EndpointChannelManager replaces the current channel, and any (or both)
|
||||
// EndpointManager methods that use a channel are running, it is better to
|
||||
// have a shared ownership.
|
||||
std::shared_ptr<EndpointChannel> GetChannelForEndpoint(
|
||||
const std::string& endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if 'endpoint_id' actually had a registered EndpointChannel.
|
||||
// IOW, a return of false signifies a no-op.
|
||||
bool unregisterChannelForEndpoint(const string& endpoint_id);
|
||||
bool UnregisterChannelForEndpoint(const std::string& endpoint_id)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
int GetConnectedEndpointsCount() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
// Tracks channel state for all endpoints. This includes what EndpointChannel
|
||||
@@ -82,64 +88,67 @@ class EndpointChannelManager {
|
||||
// been encrypted yet.
|
||||
class ChannelState {
|
||||
public:
|
||||
~ChannelState();
|
||||
struct EndpointData {
|
||||
EndpointData() = default;
|
||||
EndpointData(EndpointData&&) = default;
|
||||
EndpointData& operator=(EndpointData&&) = default;
|
||||
~EndpointData() {
|
||||
if (channel != nullptr) {
|
||||
channel->Close(disconnect_reason);
|
||||
}
|
||||
}
|
||||
|
||||
// True if we have an 'encryption_context' for the endpoint.
|
||||
bool isEndpointEncrypted(const string& endpoint_id);
|
||||
// True if we have a 'context' for the endpoint.
|
||||
bool IsEncrypted() const { return context != nullptr; }
|
||||
|
||||
// Stores a new EndpointChannel for the endpoint, returning the previous
|
||||
// one (if it existed).
|
||||
Ptr<EndpointChannel> updateChannelForEndpoint(
|
||||
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel);
|
||||
// Stores a new D2DConnectionContextV1 for the endpoint, returning the
|
||||
// previous one (if it existed).
|
||||
Ptr<securegcm::D2DConnectionContextV1> updateEncryptionContextForEndpoint(
|
||||
const string& endpoint_id,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context);
|
||||
std::shared_ptr<EndpointChannel> channel;
|
||||
std::shared_ptr<EncryptionContext> context;
|
||||
proto::connections::DisconnectionReason disconnect_reason =
|
||||
proto::connections::DisconnectionReason::UNKNOWN_DISCONNECTION_REASON;
|
||||
};
|
||||
|
||||
ChannelState() = default;
|
||||
~ChannelState() { DestroyAll(); }
|
||||
ChannelState(ChannelState&&) = default;
|
||||
ChannelState& operator=(ChannelState&&) = default;
|
||||
|
||||
// Provides a way to destroy contents of a container, while holding a lock.
|
||||
void DestroyAll() { endpoints_.clear(); }
|
||||
// Return pointer to endpoint data, or nullptr, it not found.
|
||||
EndpointData* LookupEndpointData(const std::string& endpoint_id);
|
||||
|
||||
// Stores a new EndpointChannel for the endpoint.
|
||||
// Prevoius one is destroyed, if it existed.
|
||||
void UpdateChannelForEndpoint(const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel);
|
||||
|
||||
// Stores a new EncryptionContext for the endpoint.
|
||||
// Prevoius one is destroyed, if it existed.
|
||||
void UpdateEncryptionContextForEndpoint(
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EncryptionContext> context);
|
||||
|
||||
// Removes all knowledge of this endpoint, cleaning up as necessary.
|
||||
// Returns false if the endpoint was not found.
|
||||
bool removeEndpoint(const string& endpoint_id,
|
||||
bool RemoveEndpoint(const std::string& endpoint_id,
|
||||
proto::connections::DisconnectionReason reason);
|
||||
|
||||
// Gets the 'encryption_context' for the endpoint. Null if the endpoint was
|
||||
// not found, or if there is no 'encryption_context' yet.
|
||||
Ptr<securegcm::D2DConnectionContextV1> getEncryptionContextForEndpoint(
|
||||
const string& endpoint_id);
|
||||
// Gets the 'endpoint_channel' for the endpoint. Null if the endpoint was
|
||||
// not found.
|
||||
//
|
||||
// The returned Ptr will be owned (and destroyed) by the caller.
|
||||
Ptr<EndpointChannel> getChannelForEndpoint(const string& endpoint_id);
|
||||
bool EncryptChannel(EndpointData* endpoint);
|
||||
int GetConnectedEndpointsCount() const { return endpoints_.size(); }
|
||||
|
||||
private:
|
||||
struct EndpointMetaData {
|
||||
~EndpointMetaData() {
|
||||
encryption_context.destroy();
|
||||
endpoint_channel.destroy();
|
||||
}
|
||||
|
||||
Ptr<EndpointChannel> endpoint_channel;
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context;
|
||||
};
|
||||
|
||||
// Endpoint ID -> EndpointMetadata. Contains everything we know about the
|
||||
// Endpoint ID -> EndpointData. Contains everything we know about the
|
||||
// endpoint.
|
||||
typedef std::map<string, Ptr<EndpointMetaData> > EndpointIdToMetadataMap;
|
||||
EndpointIdToMetadataMap endpoint_id_to_metadata_;
|
||||
absl::flat_hash_map<std::string, EndpointData> endpoints_;
|
||||
};
|
||||
|
||||
void setActiveEndpointChannel(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
Ptr<EndpointChannel> endpoint_channel);
|
||||
void encryptChannel(
|
||||
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<securegcm::D2DConnectionContextV1> encryption_context);
|
||||
void SetActiveEndpointChannel(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
std::unique_ptr<EndpointChannel> channel)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
ScopedPtr<Ptr<Lock> > lock_;
|
||||
|
||||
Ptr<MediumManager<Platform> > medium_manager_;
|
||||
Ptr<ChannelState> channel_state_;
|
||||
mutable Mutex mutex_;
|
||||
ChannelState channel_state_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "core_v2/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,118 +7,109 @@
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "core/listeners.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/api/count_down_latch.h"
|
||||
#include "platform/api/submittable_executor.h"
|
||||
#include "platform/api/system_clock.h"
|
||||
#include "platform/api/thread_utils.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/runnable.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/runnable.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/multi_thread_executor.h"
|
||||
#include "platform/public/single_thread_executor.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
namespace endpoint_manager {
|
||||
|
||||
template <typename>
|
||||
class ReaderRunnable;
|
||||
template <typename>
|
||||
class KeepAliveManagerRunnable;
|
||||
template <typename>
|
||||
class EndpointChannelLoopRunnable;
|
||||
template <typename>
|
||||
class RegisterIncomingOfflineFrameProcessorRunnable;
|
||||
template <typename>
|
||||
class UnregisterIncomingOfflineFrameProcessorRunnable;
|
||||
template <typename>
|
||||
class RegisterEndpointRunnable;
|
||||
template <typename>
|
||||
class UnregisterEndpointRunnable;
|
||||
template <typename>
|
||||
class DiscardEndpointRunnable;
|
||||
template <typename>
|
||||
class GetOfflineFrameProcessorCallable;
|
||||
|
||||
} // namespace endpoint_manager
|
||||
|
||||
// Manages all operations related to the remote endpoints with which we are
|
||||
// interacting.
|
||||
//
|
||||
// <p>All processing of incoming and outgoing payloads is spread across this and
|
||||
// All processing of incoming and outgoing payloads is spread across this and
|
||||
// the PayloadManager as described below.
|
||||
//
|
||||
// <p>The sending of outgoing payloads originates in
|
||||
// PayloadManager.sendPayload() before control is transferred over to
|
||||
// EndpointManager.sendPayloadChunk(). This work happens on one of three
|
||||
// The sending of outgoing payloads originates in
|
||||
// PayloadManager::SendPayload() before control is transferred over to
|
||||
// EndpointManager::SendPayloadChunk(). This work happens on one of three
|
||||
// dedicated writer threads belonging to the PayloadManager. The writer thread
|
||||
// that is used depends on the PayloadType.
|
||||
// that is used depends on the Payload::Type.
|
||||
//
|
||||
// <p>The EndpointManager has one dedicated reader thread for each registered
|
||||
// The EndpointManager has one dedicated reader thread for each registered
|
||||
// endpoint, and the receiving of every incoming payload (and its subsequent
|
||||
// chunks) originates on one of those threads before control is transferred over
|
||||
// to PayloadManager.processIncomingOfflineFrame() (still running on that
|
||||
// to PayloadManager::ProcessFrame() (still running on that
|
||||
// same dedicated reader thread).
|
||||
template <typename Platform>
|
||||
|
||||
class EndpointManager {
|
||||
public:
|
||||
class IncomingOfflineFrameProcessor {
|
||||
class FrameProcessor {
|
||||
public:
|
||||
virtual ~IncomingOfflineFrameProcessor() {}
|
||||
using Handle = void*;
|
||||
|
||||
virtual ~FrameProcessor() = default;
|
||||
|
||||
// This function takes full ownership of offline_frame.
|
||||
// @EndpointManagerReaderThread
|
||||
virtual void processIncomingOfflineFrame(
|
||||
ConstPtr<OfflineFrame> offline_frame, const string& from_endpoint_id,
|
||||
Ptr<ClientProxy<Platform> > to_client_proxy,
|
||||
proto::connections::Medium current_medium) = 0;
|
||||
// Called for every incoming frame of registered type.
|
||||
// NOTE(OfflineFrame& frame):
|
||||
// For large payload in data phase, resources may be saved if data is moved,
|
||||
// rather than copied (if passing data by reference is not an option).
|
||||
// To achieve that, OfflineFrame needs to be either mutabe lvalue reference,
|
||||
// or rvalue reference. Rvalue references are discouraged by go/cstyle,
|
||||
// and that leaves us with mutable lvalue reference.
|
||||
virtual void OnIncomingFrame(OfflineFrame& offline_frame,
|
||||
const std::string& from_endpoint_id,
|
||||
ClientProxy* to_client,
|
||||
proto::connections::Medium current_medium) = 0;
|
||||
|
||||
// Implementations must call process_disconnection_barrier.countDown() once
|
||||
// Implementations must call barrier.CountDown() once
|
||||
// they're done. This parallelizes the disconnection event across all frame
|
||||
// processors.
|
||||
//
|
||||
// @EndpointManagerThread
|
||||
virtual void processEndpointDisconnection(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
Ptr<CountDownLatch> process_disconnection_barrier) = 0;
|
||||
|
||||
// Operator overloads when comparing Ptr<IncomingOfflineFrameProcessor>.
|
||||
bool operator==(
|
||||
const typename EndpointManager<Platform>::IncomingOfflineFrameProcessor&
|
||||
rhs);
|
||||
bool operator<(
|
||||
const typename EndpointManager<Platform>::IncomingOfflineFrameProcessor&
|
||||
rhs);
|
||||
virtual void OnEndpointDisconnect(ClientProxy* client,
|
||||
const std::string& endpoint_id,
|
||||
CountDownLatch* barrier) = 0;
|
||||
};
|
||||
|
||||
explicit EndpointManager(
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager);
|
||||
explicit EndpointManager(EndpointChannelManager* manager);
|
||||
~EndpointManager();
|
||||
|
||||
// Invoked from the constructors of the various *Manager components that make
|
||||
// up the OfflineServiceController implementation.
|
||||
void registerIncomingOfflineFrameProcessor(
|
||||
V1Frame::FrameType frame_type,
|
||||
Ptr<IncomingOfflineFrameProcessor> processor);
|
||||
void unregisterIncomingOfflineFrameProcessor(
|
||||
V1Frame::FrameType frame_type,
|
||||
Ptr<IncomingOfflineFrameProcessor> processor);
|
||||
// FrameProcessor* instances are of dynamic duration and survive all sessions.
|
||||
// returns unique handle to be used for unregistering.
|
||||
// Blocks until registration is complete.
|
||||
FrameProcessor::Handle RegisterFrameProcessor(V1Frame::FrameType frame_type,
|
||||
FrameProcessor* processor);
|
||||
void UnregisterFrameProcessor(V1Frame::FrameType frame_type,
|
||||
const void* handle, bool sync = false);
|
||||
|
||||
// Invoked from the different PCPHandler implementations (of which there can
|
||||
// Invoked from the different PcpHandler implementations (of which there can
|
||||
// be only one at a time).
|
||||
void registerEndpoint(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
|
||||
const string& endpoint_name, const string& authentication_token,
|
||||
ConstPtr<ByteArray> raw_authentication_token, bool is_incoming,
|
||||
Ptr<EndpointChannel> endpoint_channel,
|
||||
Ptr<ConnectionLifecycleListener> connection_lifecycle_listener);
|
||||
// Blocks until registration is complete.
|
||||
void RegisterEndpoint(ClientProxy* client, const std::string& endpoint_id,
|
||||
const ConnectionResponseInfo& info,
|
||||
const ConnectionOptions& options,
|
||||
std::unique_ptr<EndpointChannel> channel,
|
||||
const ConnectionListener& listener);
|
||||
// Called when a client explicitly asks to disconnect from this endpoint. In
|
||||
// this case, we do not notify the client of onDisconnected().
|
||||
void unregisterEndpoint(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id);
|
||||
void UnregisterEndpoint(ClientProxy* client, const std::string& endpoint_id);
|
||||
|
||||
// Returns the list of endpoints to which sending this chunk failed.
|
||||
//
|
||||
// Invoked from the PayloadManager's sendPayload() method.
|
||||
std::vector<std::string> SendPayloadChunk(
|
||||
const PayloadTransferFrame::PayloadHeader& payload_header,
|
||||
const PayloadTransferFrame::PayloadChunk& payload_chunk,
|
||||
const std::vector<std::string>& endpoint_ids);
|
||||
std::vector<std::string> SendControlMessage(
|
||||
const PayloadTransferFrame::PayloadHeader& payload_header,
|
||||
const PayloadTransferFrame::ControlMessage& control_message,
|
||||
const std::vector<std::string>& endpoint_ids);
|
||||
|
||||
// Called when we internally want to get rid of the endpoint, without the
|
||||
// client directly telling us to. For example...
|
||||
// a) We failed to read from the endpoint in its dedicated reader thread.
|
||||
@@ -129,106 +120,106 @@ class EndpointManager {
|
||||
// believe the endpoint is in a healthy state.
|
||||
//
|
||||
// Note: This must not block. Otherwise we can get into a deadlock where we
|
||||
// ask everyone who's registered an IncomingOfflineFrameProcessor to
|
||||
// processEndpointDisconnection() while the caller of discardEndpoint() is
|
||||
// ask everyone who's registered an FrameProcessor to
|
||||
// processEndpointDisconnection() while the caller of DiscardEndpoint() is
|
||||
// blocked here.
|
||||
void discardEndpoint(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id);
|
||||
|
||||
Ptr<IncomingOfflineFrameProcessor> getOfflineFrameProcessor(
|
||||
V1Frame::FrameType frame_type);
|
||||
|
||||
// Returns the list of endpoints to which sending this chunk failed.
|
||||
//
|
||||
// Invoked from the PayloadManager's sendPayload() method.
|
||||
std::vector<string> sendPayloadChunk(
|
||||
const PayloadTransferFrame::PayloadHeader& payload_header,
|
||||
const PayloadTransferFrame::PayloadChunk& payload_chunk,
|
||||
const std::vector<string>& endpoint_ids);
|
||||
void sendControlMessage(
|
||||
const PayloadTransferFrame::PayloadHeader& payload_header,
|
||||
const PayloadTransferFrame::ControlMessage& control_message,
|
||||
const std::vector<string>& endpoint_ids);
|
||||
void DiscardEndpoint(ClientProxy* client, const std::string& endpoint_id);
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend class endpoint_manager::ReaderRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::KeepAliveManagerRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::EndpointChannelLoopRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::RegisterIncomingOfflineFrameProcessorRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::
|
||||
UnregisterIncomingOfflineFrameProcessorRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::RegisterEndpointRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::UnregisterEndpointRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::DiscardEndpointRunnable;
|
||||
template <typename>
|
||||
friend class endpoint_manager::GetOfflineFrameProcessorCallable;
|
||||
struct EndpointState {
|
||||
// ClientProxy object associated with this endpoint.
|
||||
ClientProxy* client;
|
||||
// Execution barrier, used to ensure that all workers associated with an
|
||||
// endpoint on handlers_executor_ and keep_alive_executor_ are terminated.
|
||||
CountDownLatch barrier{2};
|
||||
};
|
||||
|
||||
static void waitForLatch(const string& method_name,
|
||||
Ptr<CountDownLatch> latch);
|
||||
static void waitForLatch(const string& method_name, Ptr<CountDownLatch> latch,
|
||||
std::int32_t timeout_millis);
|
||||
template <typename T>
|
||||
static T waitForResult(const string& method_name,
|
||||
Ptr<Future<T> > result_future);
|
||||
FrameProcessor* GetFrameProcessor(V1Frame::FrameType frame_type);
|
||||
|
||||
static const std::int32_t kKeepAliveWriteIntervalMillis;
|
||||
static const std::int32_t kKeepAliveReadTimeoutMillis;
|
||||
static const std::int32_t kProcessEndpointDisconnectionTimeoutMillis;
|
||||
static const std::int32_t kMaxConcurrentEndpoints;
|
||||
static const std::int32_t kEndpointIdLength;
|
||||
ExceptionOr<bool> HandleData(const std::string& endpoint_id,
|
||||
ClientProxy* client_proxy,
|
||||
EndpointChannel* endpoint_channel);
|
||||
|
||||
ExceptionOr<bool> HandleKeepAlive(EndpointChannel* endpoint_channel);
|
||||
|
||||
// Waits for a given endpoint EndpointChannelLoopRunnable() workers to
|
||||
// terminate.
|
||||
// Is called from RegisterEndpoint to avoid races; also called from
|
||||
// RemoveEndpoint as part of proper endpoint shutdown sequence.
|
||||
// @EndpointManagerThread
|
||||
void EnsureWorkersTerminated(const std::string& endpoint_id);
|
||||
|
||||
void EndpointChannelLoopRunnable(
|
||||
const std::string& runnable_name, ClientProxy* client_proxy,
|
||||
const std::string& endpoint_id, CountDownLatch* barrier,
|
||||
std::function<ExceptionOr<bool>(EndpointChannel*)> handler);
|
||||
|
||||
static void WaitForLatch(const std::string& method_name,
|
||||
CountDownLatch* latch);
|
||||
static void WaitForLatch(const std::string& method_name,
|
||||
CountDownLatch* latch, std::int32_t timeout_millis);
|
||||
|
||||
static constexpr absl::Duration kKeepAliveWriteInterval =
|
||||
absl::Milliseconds(5000);
|
||||
static constexpr absl::Duration kKeepAliveReadTimeout =
|
||||
absl::Milliseconds(30000);
|
||||
static constexpr absl::Duration kProcessEndpointDisconnectionTimeout =
|
||||
absl::Milliseconds(2000);
|
||||
static constexpr std::int32_t kMaxConcurrentEndpoints = 50;
|
||||
static constexpr absl::Time kInvalidTimestamp = absl::InfinitePast();
|
||||
|
||||
// It should be noted that this method may be called multiple times (because
|
||||
// invoking this method closes the endpoint channel, which causes the
|
||||
// dedicated reader and KeepAlive threads to terminate, which in turn leads to
|
||||
// this method being called), but that's alright because the implementation of
|
||||
// this method is idempotent.
|
||||
void removeEndpoint(Ptr<ClientProxy<Platform> > client_proxy,
|
||||
const string& endpoint_id,
|
||||
bool send_disconnection_notification);
|
||||
// @EndpointManagerThread
|
||||
void RemoveEndpoint(ClientProxy* client, const std::string& endpoint_id,
|
||||
bool notify);
|
||||
|
||||
void waitForEndpointDisconnectionProcessing(
|
||||
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id);
|
||||
void WaitForEndpointDisconnectionProcessing(ClientProxy* client,
|
||||
const std::string& endpoint_id);
|
||||
|
||||
std::vector<string> sendTransferFrameBytes(
|
||||
const std::vector<string>& endpoint_ids,
|
||||
ConstPtr<ByteArray> payload_transfer_frame_bytes, std::int64_t payload_id,
|
||||
std::int64_t offset, const string& packet_type);
|
||||
std::vector<std::string> SendTransferFrameBytes(
|
||||
const std::vector<std::string>& endpoint_ids,
|
||||
const ByteArray& payload_transfer_frame_bytes, std::int64_t payload_id,
|
||||
std::int64_t offset, const std::string& packet_type);
|
||||
|
||||
void startEndpointReader(Ptr<Runnable> runnable);
|
||||
void startEndpointKeepAliveManager(Ptr<Runnable> runnable);
|
||||
void runOnEndpointManagerThread(Ptr<Runnable> runnable);
|
||||
template <typename T>
|
||||
Ptr<Future<T> > runOnEndpointManagerThread(Ptr<Callable<T> > callable);
|
||||
// Executes data-handing jobs on a separate thread for each endpoint, on a
|
||||
// handlers_executor_.
|
||||
// If amount of concurrent connections is less the pool capacity, it is
|
||||
// possible that while a channel is being replaced, two jobs are trying to
|
||||
// run for the same endpoint (for a short time).
|
||||
// TODO (apolyudov): do not let extra job start.
|
||||
void StartEndpointReader(Runnable runnable);
|
||||
|
||||
ScopedPtr<Ptr<ThreadUtils> > thread_utils_;
|
||||
ScopedPtr<Ptr<SystemClock> > system_clock_;
|
||||
// Executes keep-alive jobs on a separate thread for each endpoint on a
|
||||
// keep_alive_executor_.
|
||||
void StartEndpointKeepAliveManager(Runnable runnable);
|
||||
|
||||
Ptr<EndpointChannelManager> endpoint_channel_manager_;
|
||||
// Executes all jobs sequentially, on a serial_executor_.
|
||||
void RunOnEndpointManagerThread(Runnable runnable);
|
||||
|
||||
typedef std::map<V1Frame::FrameType, Ptr<IncomingOfflineFrameProcessor> >
|
||||
IncomingOfflineFrameProcessorsMap;
|
||||
IncomingOfflineFrameProcessorsMap incoming_offline_frame_processors_;
|
||||
EndpointChannelManager* channel_manager_;
|
||||
|
||||
ScopedPtr<Ptr<typename Platform::MultiThreadExecutorType> >
|
||||
endpoint_keep_alive_manager_thread_pool_;
|
||||
ScopedPtr<Ptr<typename Platform::MultiThreadExecutorType> >
|
||||
endpoint_readers_thread_pool_;
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType> > serial_executor_;
|
||||
std::shared_ptr<EndpointManager<Platform>> self_{this, [](void*){}};
|
||||
absl::flat_hash_map<V1Frame::FrameType, FrameProcessor*> frame_processors_;
|
||||
|
||||
// We keep track of all registered channel endpoints here.
|
||||
absl::flat_hash_map<std::string, EndpointState> endpoints_;
|
||||
|
||||
MultiThreadExecutor keep_alive_executor_{kMaxConcurrentEndpoints};
|
||||
MultiThreadExecutor handlers_executor_{kMaxConcurrentEndpoints};
|
||||
SingleThreadExecutor serial_executor_;
|
||||
};
|
||||
|
||||
// Operator overloads when comparing FrameProcessor*.
|
||||
bool operator==(const EndpointManager::FrameProcessor& lhs,
|
||||
const EndpointManager::FrameProcessor& rhs);
|
||||
bool operator<(const EndpointManager::FrameProcessor& lhs,
|
||||
const EndpointManager::FrameProcessor& rhs);
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/endpoint_manager.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_ENDPOINT_MANAGER_H_
|
||||
|
||||
+10
-10
@@ -1,17 +1,17 @@
|
||||
#include "core_v2/internal/endpoint_manager.h"
|
||||
#include "core/internal/endpoint_manager.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "core_v2/internal/client_proxy.h"
|
||||
#include "core_v2/internal/endpoint_channel_manager.h"
|
||||
#include "core_v2/internal/offline_frames.h"
|
||||
#include "core_v2/options.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/base/exception.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "platform_v2/public/pipe.h"
|
||||
#include "core/internal/client_proxy.h"
|
||||
#include "core/internal/endpoint_channel_manager.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "core/options.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@@ -4,16 +4,14 @@ namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
InternalPayload::InternalPayload(ConstPtr<Payload> payload)
|
||||
: payload_(payload), payload_id_(payload_->getId()) {}
|
||||
InternalPayload::InternalPayload(Payload payload)
|
||||
: payload_(std::move(payload)), payload_id_(payload_.GetId()) {}
|
||||
|
||||
InternalPayload::~InternalPayload() {}
|
||||
|
||||
ConstPtr<Payload> InternalPayload::releasePayload() {
|
||||
return payload_.release();
|
||||
Payload InternalPayload::ReleasePayload() {
|
||||
return std::move(payload_);
|
||||
}
|
||||
|
||||
std::int64_t InternalPayload::getId() const { return payload_id_; }
|
||||
Payload::Id InternalPayload::GetId() const { return payload_id_; }
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
|
||||
#include "core/payload.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/exception.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -20,12 +19,12 @@ namespace connections {
|
||||
// Payload.
|
||||
class InternalPayload {
|
||||
public:
|
||||
explicit InternalPayload(ConstPtr<Payload> payload);
|
||||
virtual ~InternalPayload();
|
||||
explicit InternalPayload(Payload payload);
|
||||
virtual ~InternalPayload() = default;
|
||||
|
||||
ConstPtr<Payload> releasePayload();
|
||||
Payload ReleasePayload();
|
||||
|
||||
std::int64_t getId() const;
|
||||
Payload::Id GetId() const;
|
||||
|
||||
// Returns the PayloadType of the Payload to which this object is bound.
|
||||
//
|
||||
@@ -34,13 +33,13 @@ class InternalPayload {
|
||||
// Payload::getType().
|
||||
//
|
||||
// @return The PayloadType.
|
||||
virtual PayloadTransferFrame::PayloadHeader::PayloadType getType() const = 0;
|
||||
virtual PayloadTransferFrame::PayloadHeader::PayloadType GetType() const = 0;
|
||||
|
||||
// Deduces the total size of the Payload to which this object is bound.
|
||||
//
|
||||
// @return The total size, or -1 if it cannot be deduced (for example, when
|
||||
// dealing with streaming data).
|
||||
virtual std::int64_t getTotalSize() const = 0;
|
||||
virtual std::int64_t GetTotalSize() const = 0;
|
||||
|
||||
// Breaks off the next chunk from the Payload to which this object is bound.
|
||||
//
|
||||
@@ -49,7 +48,7 @@ class InternalPayload {
|
||||
// a Binder, or another device altogether).
|
||||
//
|
||||
// @return The next chunk from the Payload, or null if we've reached the end.
|
||||
virtual ExceptionOr<ConstPtr<ByteArray> > detachNextChunk() = 0;
|
||||
virtual ByteArray DetachNextChunk() = 0;
|
||||
|
||||
// Adds the next chunk that comprises the Payload to which this object is
|
||||
// bound.
|
||||
@@ -61,18 +60,18 @@ class InternalPayload {
|
||||
// @param chunk The next chunk; this being null signals that this is the last
|
||||
// chunk, which will typically be used as a trigger to perform whatever state
|
||||
// cleanup may be required by the concrete implementation.
|
||||
virtual Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) = 0;
|
||||
virtual Exception AttachNextChunk(const ByteArray& chunk) = 0;
|
||||
|
||||
// Cleans up any resources used by this Payload. Called when we're stopping
|
||||
// early, e.g. after being cancelled or having no more recipients left.
|
||||
virtual void close() {}
|
||||
virtual void Close() {}
|
||||
|
||||
protected:
|
||||
ScopedPtr<ConstPtr<Payload> > payload_;
|
||||
Payload payload_;
|
||||
// We're caching the payload ID here because the backing payload will be
|
||||
// released to another owner during the lifetime of an incoming
|
||||
// InternalPayload.
|
||||
const std::int64_t payload_id_;
|
||||
Payload::Id payload_id_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include "core/internal/internal_payload_factory.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#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/pipe.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/exception.h"
|
||||
#include "platform/public/condition_variable.h"
|
||||
#include "platform/public/file.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -19,291 +20,258 @@ namespace {
|
||||
|
||||
class BytesInternalPayload : public InternalPayload {
|
||||
public:
|
||||
explicit BytesInternalPayload(ConstPtr<Payload> payload)
|
||||
: InternalPayload(payload),
|
||||
total_size_(payload_->asBytes()->size()),
|
||||
explicit BytesInternalPayload(Payload payload)
|
||||
: InternalPayload(std::move(payload)),
|
||||
total_size_(payload_.AsBytes().size()),
|
||||
detached_only_chunk_(false) {}
|
||||
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType getType() const override {
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override {
|
||||
return PayloadTransferFrame::PayloadHeader::BYTES;
|
||||
}
|
||||
|
||||
std::int64_t getTotalSize() const override { return total_size_; }
|
||||
std::int64_t GetTotalSize() const override { return total_size_; }
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray> > detachNextChunk() override {
|
||||
// Relinquishes ownership of the payload_; retrieves and returns the stored
|
||||
// ByteArray.
|
||||
ByteArray DetachNextChunk() override {
|
||||
if (detached_only_chunk_) {
|
||||
return ExceptionOr<ConstPtr<ByteArray> >(ConstPtr<ByteArray>());
|
||||
return {};
|
||||
}
|
||||
|
||||
detached_only_chunk_ = true;
|
||||
return ExceptionOr<ConstPtr<ByteArray> >(payload_->releaseBytes());
|
||||
return std::move(payload_).AsBytes();
|
||||
}
|
||||
|
||||
Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) override {
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_chunk(chunk);
|
||||
|
||||
// Nothing to do - this method makes sense for other, more long-running
|
||||
// InternalPayload concrete implementations.
|
||||
return Exception::NONE;
|
||||
// Does nothing.
|
||||
Exception AttachNextChunk(const ByteArray& chunk) override {
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
private:
|
||||
// We're caching the total size here because the backing payload will be
|
||||
// released to another owner during the lifetime of an incoming
|
||||
// moved to another owner during the lifetime of an incoming
|
||||
// InternalPayload.
|
||||
const std::int64_t total_size_;
|
||||
bool detached_only_chunk_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
class OutgoingStreamInternalPayload : public InternalPayload {
|
||||
public:
|
||||
explicit OutgoingStreamInternalPayload(ConstPtr<Payload> payload)
|
||||
: InternalPayload(payload) {}
|
||||
explicit OutgoingStreamInternalPayload(Payload payload)
|
||||
: InternalPayload(std::move(payload)) {}
|
||||
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType getType() const override {
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override {
|
||||
return PayloadTransferFrame::PayloadHeader::STREAM;
|
||||
}
|
||||
|
||||
std::int64_t getTotalSize() const override { return -1; }
|
||||
std::int64_t GetTotalSize() const override { return -1; }
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray> > detachNextChunk() override {
|
||||
Ptr<InputStream> input_stream(payload_->asStream()->asInputStream());
|
||||
ByteArray DetachNextChunk() override {
|
||||
InputStream* input_stream = payload_.AsStream();
|
||||
if (!input_stream) return {};
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray> > bytes_read =
|
||||
input_stream->read(kChunkSize);
|
||||
ExceptionOr<ByteArray> bytes_read = input_stream->Read(kChunkSize);
|
||||
if (!bytes_read.ok()) {
|
||||
if (Exception::IO == bytes_read.exception()) {
|
||||
// Ignore the potential Exception returned by close(), as a counterpart
|
||||
// to Java's closeQuietly().
|
||||
input_stream->close();
|
||||
return bytes_read;
|
||||
}
|
||||
input_stream->Close();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_bytes_read(bytes_read.result());
|
||||
ByteArray scoped_bytes_read = std::move(bytes_read.result());
|
||||
|
||||
if (scoped_bytes_read.isNull()) {
|
||||
if (scoped_bytes_read.Empty()) {
|
||||
// TODO(reznor): logger.atVerbose().log("No more data for outgoing payload
|
||||
// %s, closing InputStream.", this);
|
||||
|
||||
// Ignore the potential Exception returned by close(), as a counterpart
|
||||
// to Java's closeQuietly().
|
||||
input_stream->close();
|
||||
return ExceptionOr<ConstPtr<ByteArray> >(ConstPtr<ByteArray>());
|
||||
input_stream->Close();
|
||||
return {};
|
||||
}
|
||||
|
||||
return ExceptionOr<ConstPtr<ByteArray> >(scoped_bytes_read.release());
|
||||
return scoped_bytes_read;
|
||||
}
|
||||
|
||||
Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) override {
|
||||
return Exception::IO;
|
||||
Exception AttachNextChunk(const ByteArray& chunk) override {
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
void close() override {
|
||||
void Close() override {
|
||||
// Ignore the potential Exception returned by close(), as a counterpart
|
||||
// to Java's closeQuietly().
|
||||
payload_->asStream()->asInputStream()->close();
|
||||
InputStream* stream = payload_.AsStream();
|
||||
if (stream) stream->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::int64_t kChunkSize = 64 * 1024;
|
||||
static constexpr std::int64_t kChunkSize = Pipe::kChunkSize;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
class IncomingStreamInternalPayload : public InternalPayload {
|
||||
public:
|
||||
IncomingStreamInternalPayload(ConstPtr<Payload> payload,
|
||||
Ptr<OutputStream> output_stream)
|
||||
: InternalPayload(payload), output_stream_(output_stream) {}
|
||||
IncomingStreamInternalPayload(Payload payload, OutputStream& output_stream)
|
||||
: InternalPayload(std::move(payload)), output_stream_(&output_stream) {}
|
||||
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType getType() const override {
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override {
|
||||
return PayloadTransferFrame::PayloadHeader::STREAM;
|
||||
}
|
||||
|
||||
std::int64_t getTotalSize() const override { return -1; }
|
||||
std::int64_t GetTotalSize() const override { return -1; }
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray> > detachNextChunk() override {
|
||||
return ExceptionOr<ConstPtr<ByteArray> >(Exception::IO);
|
||||
}
|
||||
ByteArray DetachNextChunk() override { return {}; }
|
||||
|
||||
Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) override {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_chunk(chunk);
|
||||
|
||||
if (scoped_chunk.isNull()) {
|
||||
output_stream_->close();
|
||||
return Exception::NONE;
|
||||
Exception AttachNextChunk(const ByteArray& chunk) override {
|
||||
if (chunk.Empty()) {
|
||||
output_stream_->Close();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
return output_stream_->write(scoped_chunk.release());
|
||||
return output_stream_->Write(chunk);
|
||||
}
|
||||
|
||||
void close() override {
|
||||
output_stream_->close();
|
||||
}
|
||||
void Close() override { output_stream_->Close(); }
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<OutputStream> > output_stream_;
|
||||
OutputStream* output_stream_;
|
||||
};
|
||||
|
||||
class OutgoingFileInternalPayload : public InternalPayload {
|
||||
public:
|
||||
explicit OutgoingFileInternalPayload(ConstPtr<Payload> payload)
|
||||
: InternalPayload(std::move(payload)) {}
|
||||
explicit OutgoingFileInternalPayload(Payload payload)
|
||||
: InternalPayload(std::move(payload)),
|
||||
total_size_{payload_.AsFile()->GetTotalSize()} {}
|
||||
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType getType() const override {
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override {
|
||||
return PayloadTransferFrame::PayloadHeader::FILE;
|
||||
}
|
||||
|
||||
std::int64_t getTotalSize() const override {
|
||||
return payload_->asFile()->asInputFile()->getTotalSize();
|
||||
}
|
||||
std::int64_t GetTotalSize() const override { return total_size_; }
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> detachNextChunk() override {
|
||||
Ptr<InputFile> input_file(payload_->asFile()->asInputFile());
|
||||
ByteArray DetachNextChunk() override {
|
||||
InputFile* file = payload_.AsFile();
|
||||
if (!file) return {};
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> bytes_read = input_file->read(kChunkSize);
|
||||
ExceptionOr<ByteArray> bytes_read = file->Read(kChunkSize);
|
||||
if (!bytes_read.ok()) {
|
||||
if (Exception::IO == bytes_read.exception()) {
|
||||
input_file->close();
|
||||
return bytes_read;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_bytes_read(bytes_read.result());
|
||||
ByteArray bytes = std::move(bytes_read.result());
|
||||
|
||||
if (scoped_bytes_read.isNull()) {
|
||||
if (bytes.Empty()) {
|
||||
// No more data for outgoing payload.
|
||||
|
||||
input_file->close();
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(ConstPtr<ByteArray>());
|
||||
file->Close();
|
||||
return {};
|
||||
}
|
||||
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(scoped_bytes_read.release());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) override {
|
||||
return Exception::IO;
|
||||
Exception AttachNextChunk(const ByteArray& chunk) override {
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
void close() override { payload_->asFile()->asInputFile()->close(); }
|
||||
void Close() override {
|
||||
InputFile* file = payload_.AsFile();
|
||||
if (file) file->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
std::int64_t total_size_;
|
||||
static constexpr std::int64_t kChunkSize = 64 * 1024;
|
||||
};
|
||||
|
||||
class IncomingFileInternalPayload : public InternalPayload {
|
||||
public:
|
||||
IncomingFileInternalPayload(ConstPtr<Payload> payload,
|
||||
const Ptr<OutputFile>& output_file,
|
||||
IncomingFileInternalPayload(Payload payload, OutputFile output_file,
|
||||
std::int64_t total_size)
|
||||
: InternalPayload(std::move(payload)),
|
||||
output_file_(output_file),
|
||||
output_file_(std::move(output_file)),
|
||||
total_size_(total_size) {}
|
||||
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType getType() const override {
|
||||
PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override {
|
||||
return PayloadTransferFrame::PayloadHeader::FILE;
|
||||
}
|
||||
|
||||
std::int64_t getTotalSize() const override { return total_size_; }
|
||||
std::int64_t GetTotalSize() const override { return total_size_; }
|
||||
|
||||
ExceptionOr<ConstPtr<ByteArray>> detachNextChunk() override {
|
||||
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
|
||||
}
|
||||
ByteArray DetachNextChunk() override { return {}; }
|
||||
|
||||
Exception::Value attachNextChunk(ConstPtr<ByteArray> chunk) override {
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_chunk(chunk);
|
||||
|
||||
if (scoped_chunk.isNull()) {
|
||||
Exception AttachNextChunk(const ByteArray& chunk) override {
|
||||
if (chunk.Empty()) {
|
||||
// Received null last chunk for incoming payload.
|
||||
output_file_->close();
|
||||
return Exception::NONE;
|
||||
output_file_.Close();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
return output_file_->write(scoped_chunk.release());
|
||||
return output_file_.Write(chunk);
|
||||
}
|
||||
|
||||
void close() override { output_file_->close(); }
|
||||
void Close() override { output_file_.Close(); }
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<OutputFile>> output_file_;
|
||||
OutputFile output_file_;
|
||||
const std::int64_t total_size_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<InternalPayload> InternalPayloadFactory<Platform>::createOutgoing(
|
||||
ConstPtr<Payload> payload) {
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<Payload> > scoped_payload(payload);
|
||||
std::unique_ptr<InternalPayload> CreateOutgoingInternalPayload(
|
||||
Payload payload) {
|
||||
switch (payload.GetType()) {
|
||||
case Payload::Type::kBytes:
|
||||
return absl::make_unique<BytesInternalPayload>(std::move(payload));
|
||||
|
||||
switch (scoped_payload->getType()) {
|
||||
case Payload::Type::BYTES:
|
||||
return MakePtr(new BytesInternalPayload(scoped_payload.release()));
|
||||
case Payload::Type::kFile: {
|
||||
InputFile* file = payload.AsFile();
|
||||
const PayloadId file_payload_id = file ? file->GetPayloadId() : 0;
|
||||
const PayloadId payload_id = payload.GetId();
|
||||
CHECK(payload_id == file_payload_id);
|
||||
return absl::make_unique<OutgoingFileInternalPayload>(std::move(payload));
|
||||
}
|
||||
|
||||
case Payload::Type::FILE:
|
||||
return MakePtr(new OutgoingFileInternalPayload(scoped_payload.release()));
|
||||
case Payload::Type::kStream:
|
||||
return absl::make_unique<OutgoingStreamInternalPayload>(
|
||||
std::move(payload));
|
||||
|
||||
case Payload::Type::STREAM:
|
||||
return MakePtr(new OutgoingStreamInternalPayload<Platform>(
|
||||
scoped_payload.release()));
|
||||
|
||||
default: {}
|
||||
// Fall through
|
||||
default:
|
||||
DCHECK(false); // This should never happen.
|
||||
return {};
|
||||
}
|
||||
|
||||
// This should never be reached since the ServiceControllerRouter has already
|
||||
// checked whether or not we can work with this Payload type.
|
||||
return Ptr<InternalPayload>();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<InternalPayload> InternalPayloadFactory<Platform>::createIncoming(
|
||||
const PayloadTransferFrame& payload_transfer_frame) {
|
||||
if (PayloadTransferFrame::DATA != payload_transfer_frame.packet_type()) {
|
||||
return Ptr<InternalPayload>();
|
||||
std::unique_ptr<InternalPayload> CreateIncomingInternalPayload(
|
||||
const PayloadTransferFrame& frame) {
|
||||
if (frame.packet_type() != PayloadTransferFrame::DATA) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int64_t payload_id = payload_transfer_frame.payload_header().id();
|
||||
switch (payload_transfer_frame.payload_header().type()) {
|
||||
const Payload::Id payload_id = frame.payload_header().id();
|
||||
switch (frame.payload_header().type()) {
|
||||
case PayloadTransferFrame::PayloadHeader::BYTES: {
|
||||
const string& body = payload_transfer_frame.payload_chunk().body();
|
||||
return MakePtr(new BytesInternalPayload(MakeConstPtr(new Payload(
|
||||
payload_id, MakeConstPtr(new ByteArray(body.data(), body.size()))))));
|
||||
return absl::make_unique<BytesInternalPayload>(
|
||||
Payload(payload_id, ByteArray(frame.payload_chunk().body())));
|
||||
}
|
||||
|
||||
case PayloadTransferFrame::PayloadHeader::STREAM: {
|
||||
// pipe will be auto-destroyed when it is no longer referenced.
|
||||
auto pipe = MakeRefCountedPtr(new Pipe());
|
||||
auto pipe = std::make_shared<Pipe>();
|
||||
|
||||
return MakePtr(new IncomingStreamInternalPayload<Platform>(
|
||||
MakeConstPtr(
|
||||
new Payload(payload_id, MakeConstPtr(new Payload::Stream(
|
||||
Pipe::createInputStream(pipe))))),
|
||||
Pipe::createOutputStream(pipe)));
|
||||
return absl::make_unique<IncomingStreamInternalPayload>(
|
||||
Payload(payload_id,
|
||||
[pipe]() -> InputStream& {
|
||||
return pipe->GetInputStream(); // NOLINT
|
||||
}),
|
||||
pipe->GetOutputStream());
|
||||
}
|
||||
|
||||
case PayloadTransferFrame::PayloadHeader::FILE: {
|
||||
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(
|
||||
payload, output_file,
|
||||
payload_transfer_frame.payload_header().total_size()));
|
||||
std::int64_t total_size = frame.payload_header().total_size();
|
||||
return absl::make_unique<IncomingFileInternalPayload>(
|
||||
Payload(payload_id, InputFile(payload_id, total_size)),
|
||||
OutputFile(payload_id), total_size);
|
||||
}
|
||||
default: {}
|
||||
// Fall through.
|
||||
default:
|
||||
DCHECK(false); // This should never happen.
|
||||
return {};
|
||||
}
|
||||
|
||||
// This should never be reached since the ServiceControllerRouter has
|
||||
// already checked whether or not we can work with this Payload type.
|
||||
return Ptr<InternalPayload>();
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -4,31 +4,21 @@
|
||||
#include "core/internal/internal_payload.h"
|
||||
#include "core/payload.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
class InternalPayloadFactory {
|
||||
public:
|
||||
// Creates an InternalPayload representing an outgoing Payload.
|
||||
//
|
||||
// The returned Ptr<InternalPayload> will take ownership of the passed-in
|
||||
// 'payload'.
|
||||
Ptr<InternalPayload> createOutgoing(ConstPtr<Payload> payload);
|
||||
// Creates an InternalPayload representing an outgoing Payload.
|
||||
std::unique_ptr<InternalPayload> CreateOutgoingInternalPayload(Payload payload);
|
||||
|
||||
// Creates an InternalPayload representing an incoming Payload from a remote
|
||||
// endpoint.
|
||||
Ptr<InternalPayload> createIncoming(
|
||||
const PayloadTransferFrame& payload_transfer_frame);
|
||||
};
|
||||
// Creates an InternalPayload representing an incoming Payload from a remote
|
||||
// endpoint.
|
||||
std::unique_ptr<InternalPayload> CreateIncomingInternalPayload(
|
||||
const PayloadTransferFrame& frame);
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/internal_payload_factory.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_INTERNAL_PAYLOAD_FACTORY_H_
|
||||
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
#include "core_v2/internal/internal_payload_factory.h"
|
||||
#include "core/internal/internal_payload_factory.h"
|
||||
|
||||
#include "core_v2/internal/offline_frames.h"
|
||||
#include "core/internal/offline_frames.h"
|
||||
#include "proto/connections/offline_wire_formats.pb.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/pipe.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/pipe.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "core/internal/loop_runner.h"
|
||||
|
||||
#include "platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
LoopRunner::LoopRunner(const std::string& name) : name_(name) {}
|
||||
|
||||
bool LoopRunner::loop(Ptr<Callable<bool> > callable) {
|
||||
ScopedPtr<Ptr<Callable<bool> > > scoped_callable(callable);
|
||||
|
||||
onEnterLoop();
|
||||
while (true) {
|
||||
onEnterIteration();
|
||||
ExceptionOr<bool> should_continue = scoped_callable->call();
|
||||
if (!should_continue.ok()) {
|
||||
onExceptionExitLoop(should_continue.exception());
|
||||
break;
|
||||
}
|
||||
|
||||
onExitIteration();
|
||||
if (!should_continue.result()) {
|
||||
onExitLoop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoopRunner::onEnterLoop() {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
void LoopRunner::onEnterIteration() {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
void LoopRunner::onExitIteration() {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
void LoopRunner::onExitLoop() {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
void LoopRunner::onExceptionExitLoop(Exception::Value exception) {
|
||||
// TODO(tracyzhou): Add logging.
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_LOOP_RUNNER_H_
|
||||
#define CORE_INTERNAL_LOOP_RUNNER_H_
|
||||
|
||||
#include "platform/callable.h"
|
||||
#include "platform/exception.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
// Construct to run a loop repeatedly. This class is useful to increase
|
||||
// testability for multi-threaded code that runs loops; it shouldn't be used for
|
||||
// general purpose loops unless tests require fine-grained control over the
|
||||
// looping procedure.
|
||||
class LoopRunner {
|
||||
public:
|
||||
explicit LoopRunner(const std::string& name);
|
||||
|
||||
// Runs the provided callable repeatedly until it returns false.
|
||||
//
|
||||
// @return true if the loop completed successfully, false if an exception was
|
||||
// encountered.
|
||||
bool loop(Ptr<Callable<bool> > callable);
|
||||
|
||||
protected:
|
||||
void onEnterLoop();
|
||||
void onEnterIteration();
|
||||
void onExitIteration();
|
||||
void onExitLoop();
|
||||
void onExceptionExitLoop(Exception::Value exception);
|
||||
|
||||
private:
|
||||
const std::string name_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_LOOP_RUNNER_H_
|
||||
@@ -1,488 +0,0 @@
|
||||
#include "core/internal/medium_manager.h"
|
||||
|
||||
#include "platform/synchronized.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
MediumManager<Platform>::MediumManager()
|
||||
: mediums_(new Mediums<Platform>()),
|
||||
bluetooth_classic_lock_(Platform::createLock()),
|
||||
ble_lock_(Platform::createLock()),
|
||||
wifi_lan_lock_(Platform::createLock()) {}
|
||||
|
||||
template <typename Platform>
|
||||
MediumManager<Platform>::~MediumManager() {
|
||||
// TODO(reznor): log.atDebug().log("Initiating shutdown of MediumManager.");
|
||||
Synchronized s1(bluetooth_classic_lock_.get());
|
||||
Synchronized s2(ble_lock_.get());
|
||||
Synchronized s3(wifi_lan_lock_.get());
|
||||
|
||||
mediums_.destroy();
|
||||
// TODO(reznor): log.atDebug().log("MediumManager has shut down.");
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ BLUETOOTH ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::isBluetoothAvailable() {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
return mediums_->bluetoothClassic()->isAvailable();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::turnOnBluetoothDiscoverability(
|
||||
const string& device_name) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
mediums_->bluetoothClassic()->turnOnDiscoverability(device_name);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::turnOffBluetoothDiscoverability() {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
mediums_->bluetoothClassic()->turnOffDiscoverability();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class DiscoveredDeviceCallback
|
||||
: public BluetoothClassic<Platform>::DiscoveredDeviceCallback {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::FoundBluetoothDeviceProcessor
|
||||
FoundBluetoothDeviceProcessor;
|
||||
|
||||
explicit DiscoveredDeviceCallback(
|
||||
Ptr<FoundBluetoothDeviceProcessor> found_bluetooth_device_processor)
|
||||
: found_bluetooth_device_processor_(found_bluetooth_device_processor) {}
|
||||
|
||||
void onDeviceDiscovered(Ptr<BluetoothDevice> device) override {
|
||||
found_bluetooth_device_processor_->onFoundBluetoothDevice(device);
|
||||
}
|
||||
|
||||
void onDeviceNameChanged(Ptr<BluetoothDevice> device) override {
|
||||
found_bluetooth_device_processor_->onFoundBluetoothDevice(device);
|
||||
}
|
||||
|
||||
void onDeviceLost(Ptr<BluetoothDevice> device) override {
|
||||
found_bluetooth_device_processor_->onLostBluetoothDevice(device);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<FoundBluetoothDeviceProcessor> >
|
||||
found_bluetooth_device_processor_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::startScanningForBluetoothDevices(
|
||||
Ptr<FoundBluetoothDeviceProcessor> found_bluetooth_device_processor) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
mediums_->bluetoothClassic()->startDiscovery(
|
||||
MakePtr(new DiscoveredDeviceCallback<Platform>(
|
||||
found_bluetooth_device_processor)));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::stopScanningForBluetoothDevices() {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
mediums_->bluetoothClassic()->stopDiscovery();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::isListeningForIncomingBluetoothConnections(
|
||||
const string& service_name) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
return mediums_->bluetoothClassic()->isAcceptingConnections(service_name);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class BluetoothAcceptedConnectionCallback
|
||||
: public BluetoothClassic<Platform>::AcceptedConnectionCallback {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::IncomingBluetoothConnectionProcessor
|
||||
IncomingBluetoothConnectionProcessor;
|
||||
|
||||
explicit BluetoothAcceptedConnectionCallback(
|
||||
Ptr<IncomingBluetoothConnectionProcessor>
|
||||
incoming_bluetooth_connection_processor)
|
||||
: incoming_bluetooth_connection_processor_(
|
||||
incoming_bluetooth_connection_processor) {}
|
||||
|
||||
void onConnectionAccepted(Ptr<BluetoothSocket> socket) override {
|
||||
incoming_bluetooth_connection_processor_->onIncomingBluetoothConnection(
|
||||
socket);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<IncomingBluetoothConnectionProcessor> >
|
||||
incoming_bluetooth_connection_processor_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::startListeningForIncomingBluetoothConnections(
|
||||
const string& service_name, Ptr<IncomingBluetoothConnectionProcessor>
|
||||
incoming_bluetooth_connection_processor) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
mediums_->bluetoothClassic()->startAcceptingConnections(
|
||||
service_name,
|
||||
MakePtr(new BluetoothAcceptedConnectionCallback<Platform>(
|
||||
incoming_bluetooth_connection_processor)));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::stopListeningForIncomingBluetoothConnections(
|
||||
const string& service_name) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
mediums_->bluetoothClassic()->stopAcceptingConnections(service_name);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<BluetoothSocket> MediumManager<Platform>::connectToBluetoothDevice(
|
||||
Ptr<BluetoothDevice> bluetooth_device, const string& service_name) {
|
||||
Synchronized s(bluetooth_classic_lock_.get());
|
||||
|
||||
if (!mediums_->bluetoothRadio()->enable()) {
|
||||
return Ptr<BluetoothSocket>();
|
||||
}
|
||||
|
||||
return mediums_->bluetoothClassic()->connect(bluetooth_device, service_name);
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ BLE ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::isBleAvailable() {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
return mediums_->bleV2()->isAvailable();
|
||||
#else
|
||||
return mediums_->ble()->isAvailable();
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(ahlee): Add nearbyNotificationsBeaconData for phase 2 of implementation.
|
||||
// TODO(ahlee): Add fast_advertisement_service_uuid and power_level to
|
||||
// AdvertisingOptions and pass it through.
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::startBleAdvertising(
|
||||
const string& service_id, ConstPtr<ByteArray> advertisement_data) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->startAdvertising(
|
||||
service_id, advertisement_data, BLEMediumV2::PowerMode::HIGH,
|
||||
/* fast_advertisement_service_uuid= */ "");
|
||||
#else
|
||||
mediums_->ble()->startAdvertising(service_id, advertisement_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::stopBleAdvertising(const string& service_id) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->stopAdvertising();
|
||||
#else
|
||||
mediums_->ble()->stopAdvertising();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
template <typename Platform>
|
||||
class BLEAcceptedConnectionCallback
|
||||
: public mediums::BLEV2<Platform>::AcceptedConnectionCallback {
|
||||
public:
|
||||
BLEAcceptedConnectionCallback() {}
|
||||
};
|
||||
#else
|
||||
template <typename Platform>
|
||||
class BLEAcceptedConnectionCallback
|
||||
: public BLE<Platform>::AcceptedConnectionCallback {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::IncomingBleConnectionProcessor
|
||||
IncomingBleConnectionProcessor;
|
||||
|
||||
explicit BLEAcceptedConnectionCallback(
|
||||
Ptr<IncomingBleConnectionProcessor> incoming_ble_connection_processor)
|
||||
: incoming_ble_connection_processor_(incoming_ble_connection_processor) {}
|
||||
|
||||
void onConnectionAccepted(Ptr<BLESocket> socket,
|
||||
const string& service_id) override {
|
||||
incoming_ble_connection_processor_->onIncomingBleConnection(socket,
|
||||
service_id);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<IncomingBleConnectionProcessor> >
|
||||
incoming_ble_connection_processor_;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::isListeningForIncomingBleConnections(
|
||||
const string& service_id) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
return mediums_->bleV2()->isAcceptingConnections();
|
||||
#else
|
||||
return mediums_->ble()->isAcceptingConnections();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::startListeningForIncomingBleConnections(
|
||||
const string& service_id,
|
||||
Ptr<IncomingBleConnectionProcessor> incoming_ble_connection_processor) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->startAcceptingConnections(
|
||||
service_id,
|
||||
MakePtr(new BLEAcceptedConnectionCallback<Platform>()));
|
||||
#else
|
||||
mediums_->ble()->startAcceptingConnections(
|
||||
service_id, MakePtr(new BLEAcceptedConnectionCallback<Platform>(
|
||||
incoming_ble_connection_processor)));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::stopListeningForIncomingBleConnections(
|
||||
const string& service_id) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->stopAcceptingConnections();
|
||||
#else
|
||||
mediums_->ble()->stopAcceptingConnections();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class DiscoveredPeripheralCallback : public DISCOVERED_PERIPHERAL_CALLBACK {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::FoundBlePeripheralProcessor
|
||||
FoundBlePeripheralProcessor;
|
||||
|
||||
explicit DiscoveredPeripheralCallback(
|
||||
Ptr<FoundBlePeripheralProcessor> found_ble_peripheral_processor)
|
||||
: found_ble_peripheral_processor_(found_ble_peripheral_processor) {}
|
||||
|
||||
void onPeripheralDiscovered(Ptr<BLE_PERIPHERAL> ble_peripheral,
|
||||
const string& service_id,
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
ConstPtr<ByteArray> advertisement_data,
|
||||
// TODO(ahlee): Add is_fast_advertisement to
|
||||
// FoundBlePeripheralProcessor.
|
||||
bool is_fast_advertisement) override {
|
||||
#else
|
||||
ConstPtr<ByteArray> advertisement_data) {
|
||||
#endif
|
||||
found_ble_peripheral_processor_->onFoundBlePeripheral(
|
||||
ble_peripheral, service_id, advertisement_data);
|
||||
}
|
||||
|
||||
void onPeripheralLost(Ptr<BLE_PERIPHERAL> ble_peripheral,
|
||||
const string& service_id) override {
|
||||
found_ble_peripheral_processor_->onLostBlePeripheral(ble_peripheral,
|
||||
service_id);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<FoundBlePeripheralProcessor> > found_ble_peripheral_processor_;
|
||||
};
|
||||
|
||||
// TODO(ahlee): Add fast_advertisement_service_uuid and power_level to
|
||||
// DiscoveryOptions and pass it through.
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::startBleScanning(
|
||||
const string& service_id,
|
||||
Ptr<FoundBlePeripheralProcessor> found_ble_peripheral_processor) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
return mediums_->bluetoothRadio()->enable() &&
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->startScanning(
|
||||
service_id,
|
||||
MakePtr(new DiscoveredPeripheralCallback<Platform>(
|
||||
found_ble_peripheral_processor)),
|
||||
BLEMediumV2::PowerMode::HIGH,
|
||||
/* fast_advertisement_service_uuid= */ "");
|
||||
#else
|
||||
mediums_->ble()->startScanning(
|
||||
service_id, MakePtr(new DiscoveredPeripheralCallback<Platform>(
|
||||
found_ble_peripheral_processor)));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::stopBleScanning(const string& service_id) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
mediums_->bleV2()->stopScanning();
|
||||
#else
|
||||
mediums_->ble()->stopScanning();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<BLESocket> MediumManager<Platform>::connectToBlePeripheral(
|
||||
Ptr<BLE_PERIPHERAL> ble_peripheral, const string& service_id) {
|
||||
Synchronized s(ble_lock_.get());
|
||||
|
||||
if (!mediums_->bluetoothRadio()->enable()) {
|
||||
return Ptr<BLESocket>();
|
||||
}
|
||||
|
||||
#if BLE_V2_IMPLEMENTED
|
||||
// TODO(ahlee): Replace when connecting logic is implemented.
|
||||
return Ptr<BLESocket>();
|
||||
#else
|
||||
return mediums_->ble()->connect(ble_peripheral, service_id);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ WIFILAN ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::IsWifiLanAvailable() {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->IsAvailable();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::StartWifiLanAdvertising(
|
||||
absl::string_view service_id, absl::string_view service_info_name) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->StartAdvertising(service_id, service_info_name);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::StopWifiLanAdvertising(
|
||||
absl::string_view service_id) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
mediums_->wifi_lan()->StopAdvertising(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class DiscoveredServiceCallback : public mediums::DiscoveredServiceCallback {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::FoundWifiLanServiceProcessor
|
||||
FoundWifiLanServiceProcessor;
|
||||
|
||||
explicit DiscoveredServiceCallback(
|
||||
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor)
|
||||
: found_wifi_lan_service_processor_(found_wifi_lan_service_processor) {}
|
||||
|
||||
void OnServiceDiscovered(Ptr<WifiLanService> wifi_lan_service) override {
|
||||
found_wifi_lan_service_processor_->OnFoundWifiLanService(wifi_lan_service);
|
||||
}
|
||||
|
||||
void OnServiceLost(Ptr<WifiLanService> wifi_lan_service) override {
|
||||
found_wifi_lan_service_processor_->OnLostWifiLanService(wifi_lan_service);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<FoundWifiLanServiceProcessor> >
|
||||
found_wifi_lan_service_processor_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::StartWifiLanDiscovery(
|
||||
absl::string_view service_id,
|
||||
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->StartDiscovery(
|
||||
service_id, MakePtr(new DiscoveredServiceCallback<Platform>(
|
||||
found_wifi_lan_service_processor)));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::StopWifiLanDiscovery(
|
||||
absl::string_view service_id) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
mediums_->wifi_lan()->StopDiscovery(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
class WifiLanAcceptedConnectionCallback
|
||||
: public mediums::WifiLan<Platform>::AcceptedConnectionCallback {
|
||||
public:
|
||||
typedef typename MediumManager<Platform>::IncomingWifiLanConnectionProcessor
|
||||
IncomingWifiLanConnectionProcessor;
|
||||
|
||||
explicit WifiLanAcceptedConnectionCallback(
|
||||
Ptr<IncomingWifiLanConnectionProcessor>
|
||||
incoming_wifi_lan_connection_processor)
|
||||
: incoming_wifi_lan_connection_processor_(
|
||||
incoming_wifi_lan_connection_processor) {}
|
||||
|
||||
void OnConnectionAccepted(Ptr<WifiLanSocket> socket,
|
||||
absl::string_view service_id) override {
|
||||
incoming_wifi_lan_connection_processor_->OnIncomingWifiLanConnection(
|
||||
socket);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<IncomingWifiLanConnectionProcessor> >
|
||||
incoming_wifi_lan_connection_processor_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::IsListeningForIncomingWifiLanConnections(
|
||||
absl::string_view service_id) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->IsAcceptingConnections(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool MediumManager<Platform>::StartListeningForIncomingWifiLanConnections(
|
||||
absl::string_view service_id, Ptr<IncomingWifiLanConnectionProcessor>
|
||||
incoming_wifi_lan_connection_processor) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->StartAcceptingConnections(
|
||||
service_id, MakePtr(new WifiLanAcceptedConnectionCallback<Platform>(
|
||||
incoming_wifi_lan_connection_processor)));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void MediumManager<Platform>::StopListeningForIncomingWifiLanConnections(
|
||||
absl::string_view service_id) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
mediums_->wifi_lan()->StopAcceptingConnections(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<WifiLanSocket> MediumManager<Platform>::ConnectToWifiLanService(
|
||||
Ptr<WifiLanService> wifi_lan_service, absl::string_view service_id) {
|
||||
Synchronized s(wifi_lan_lock_.get());
|
||||
|
||||
return mediums_->wifi_lan()->Connect(wifi_lan_service, service_id);
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,180 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUM_MANAGER_H_
|
||||
#define CORE_INTERNAL_MEDIUM_MANAGER_H_
|
||||
|
||||
#include "core/internal/ble_compat.h"
|
||||
#include "core/internal/mediums/mediums.h"
|
||||
#include "platform/api/bluetooth_classic.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/**
|
||||
* Manages everything related to the mediums used by Nearby Connections, acting
|
||||
* as a simplifying layer around the different APIs used for said management.
|
||||
*
|
||||
* <p>An overview of thread safety:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Methods are synchronized at a per-medium level. For example, all
|
||||
* Bluetooth Classic calls are synchronized under the same
|
||||
* 'bluetooth_classic_lock_'. This ensures work on a particular medium is
|
||||
* well-ordered without blocking other mediums from running. Nearby
|
||||
* Mediums as a whole is already threadsafe, which is why we don't need to
|
||||
* synchronize at a per-radio level.
|
||||
* <li>All calls are guarded by the flag 'mediums_are_available_', which
|
||||
* defaults to true and is set to false in shutdown(). This flag ensures
|
||||
* that no further work is done after shutdown() has been called.
|
||||
* Note: shutdown() is the one and only time we grab every
|
||||
* medium-specific lock, to ensure everything stops at once.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note: For methods that start an action (eg. startAdvertising()), the radio
|
||||
* is first enabled. This is a prerequisite before doing any work on a medium;
|
||||
* they will otherwise fail if the radio is off. Calls that stop an action (eg.
|
||||
* stopAdvertising()) do not attempt to enable the radio because, if the radio
|
||||
* was off, there is no work for them to stop.
|
||||
*/
|
||||
template <typename Platform>
|
||||
class MediumManager {
|
||||
public:
|
||||
MediumManager();
|
||||
~MediumManager();
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ BLUETOOTH ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool isBluetoothAvailable();
|
||||
|
||||
bool turnOnBluetoothDiscoverability(const string& device_name);
|
||||
void turnOffBluetoothDiscoverability();
|
||||
|
||||
class FoundBluetoothDeviceProcessor {
|
||||
public:
|
||||
virtual ~FoundBluetoothDeviceProcessor() {}
|
||||
|
||||
virtual void onFoundBluetoothDevice(
|
||||
Ptr<BluetoothDevice> bluetooth_device) = 0;
|
||||
virtual void onLostBluetoothDevice(
|
||||
Ptr<BluetoothDevice> bluetooth_device) = 0;
|
||||
};
|
||||
|
||||
bool startScanningForBluetoothDevices(
|
||||
Ptr<FoundBluetoothDeviceProcessor> found_bluetooth_device_processor);
|
||||
void stopScanningForBluetoothDevices();
|
||||
|
||||
class IncomingBluetoothConnectionProcessor {
|
||||
public:
|
||||
virtual ~IncomingBluetoothConnectionProcessor() {}
|
||||
|
||||
virtual void onIncomingBluetoothConnection(
|
||||
Ptr<BluetoothSocket> bluetooth_socket) = 0;
|
||||
};
|
||||
|
||||
bool isListeningForIncomingBluetoothConnections(const string& service_name);
|
||||
bool startListeningForIncomingBluetoothConnections(
|
||||
const string& service_name, Ptr<IncomingBluetoothConnectionProcessor>
|
||||
incoming_bluetooth_connection_processor);
|
||||
void stopListeningForIncomingBluetoothConnections(const string& service_name);
|
||||
|
||||
Ptr<BluetoothSocket> connectToBluetoothDevice(
|
||||
Ptr<BluetoothDevice> bluetooth_device, const string& service_name);
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ BLE ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
bool isBleAvailable();
|
||||
|
||||
bool startBleAdvertising(const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement_data);
|
||||
void stopBleAdvertising(const string& service_id);
|
||||
|
||||
class IncomingBleConnectionProcessor {
|
||||
public:
|
||||
virtual ~IncomingBleConnectionProcessor() {}
|
||||
|
||||
virtual void onIncomingBleConnection(Ptr<BLESocket> ble_socket,
|
||||
const string& service_id) = 0;
|
||||
};
|
||||
|
||||
bool isListeningForIncomingBleConnections(const string& service_id);
|
||||
bool startListeningForIncomingBleConnections(
|
||||
const string& service_id,
|
||||
Ptr<IncomingBleConnectionProcessor> incoming_ble_connection_processor);
|
||||
void stopListeningForIncomingBleConnections(const string& service_id);
|
||||
|
||||
class FoundBlePeripheralProcessor {
|
||||
public:
|
||||
virtual ~FoundBlePeripheralProcessor() {}
|
||||
|
||||
virtual void onFoundBlePeripheral(
|
||||
Ptr<BLE_PERIPHERAL> ble_peripheral, const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement_data) = 0;
|
||||
virtual void onLostBlePeripheral(Ptr<BLE_PERIPHERAL> ble_peripheral,
|
||||
const string& service_id) = 0;
|
||||
};
|
||||
|
||||
bool startBleScanning(
|
||||
const string& service_id,
|
||||
Ptr<FoundBlePeripheralProcessor> found_ble_peripheral_processor);
|
||||
void stopBleScanning(const string& service_id);
|
||||
|
||||
Ptr<BLESocket> connectToBlePeripheral(Ptr<BLE_PERIPHERAL> ble_peripheral,
|
||||
const string& service_id);
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~ WIFI-LAN ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
bool IsWifiLanAvailable();
|
||||
|
||||
bool StartWifiLanAdvertising(absl::string_view service_id,
|
||||
absl::string_view wifi_lan_service_info_name);
|
||||
void StopWifiLanAdvertising(absl::string_view service_id);
|
||||
|
||||
class FoundWifiLanServiceProcessor {
|
||||
public:
|
||||
virtual ~FoundWifiLanServiceProcessor() {}
|
||||
|
||||
virtual void OnFoundWifiLanService(
|
||||
Ptr<WifiLanService> wifi_lan_service) = 0;
|
||||
virtual void OnLostWifiLanService(Ptr<WifiLanService> wifi_lan_service) = 0;
|
||||
};
|
||||
|
||||
bool StartWifiLanDiscovery(
|
||||
absl::string_view service_id,
|
||||
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor);
|
||||
void StopWifiLanDiscovery(absl::string_view service_id);
|
||||
|
||||
class IncomingWifiLanConnectionProcessor {
|
||||
public:
|
||||
virtual ~IncomingWifiLanConnectionProcessor() {}
|
||||
|
||||
virtual void OnIncomingWifiLanConnection(
|
||||
Ptr<WifiLanSocket> wifi_lan_socket) = 0;
|
||||
};
|
||||
|
||||
bool IsListeningForIncomingWifiLanConnections(absl::string_view service_id);
|
||||
bool StartListeningForIncomingWifiLanConnections(
|
||||
absl::string_view service_id, Ptr<IncomingWifiLanConnectionProcessor>
|
||||
incoming_wifi_lan_connection_processor);
|
||||
void StopListeningForIncomingWifiLanConnections(absl::string_view service_id);
|
||||
|
||||
Ptr<WifiLanSocket> ConnectToWifiLanService(
|
||||
Ptr<WifiLanService> wifi_lan_service, absl::string_view service_id);
|
||||
|
||||
private:
|
||||
// The destructor for this needs to be manually invoked after the locks below
|
||||
// are acquired, so it cannot be a ScopedPtr.
|
||||
Ptr<Mediums<Platform> > mediums_;
|
||||
|
||||
ScopedPtr<Ptr<Lock> > bluetooth_classic_lock_;
|
||||
ScopedPtr<Ptr<Lock> > ble_lock_;
|
||||
ScopedPtr<Ptr<Lock> > wifi_lan_lock_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/medium_manager.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUM_MANAGER_H_
|
||||
+64
-105
@@ -1,134 +1,93 @@
|
||||
cc_library(
|
||||
name = "utils",
|
||||
srcs = [
|
||||
"utils.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"utils.h",
|
||||
],
|
||||
visibility = [
|
||||
"//core/internal/mediums/webrtc:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//platform:types",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/port:string",
|
||||
"//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "mediums",
|
||||
srcs = [
|
||||
"ble_advertisement.cc",
|
||||
"ble_advertisement_header.cc",
|
||||
"ble_packet.cc",
|
||||
"ble_peripheral.cc",
|
||||
"ble.cc",
|
||||
"bloom_filter.cc",
|
||||
"bluetooth_classic.cc",
|
||||
"bluetooth_radio.cc",
|
||||
"mediums.cc",
|
||||
"uuid.cc",
|
||||
"webrtc.cc",
|
||||
"wifi_lan.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"advertisement_read_result.cc",
|
||||
"advertisement_read_result.h",
|
||||
"ble.cc",
|
||||
"ble.h",
|
||||
"ble_advertisement.h",
|
||||
"ble_advertisement_header.h",
|
||||
"ble_packet.h",
|
||||
"ble_peripheral.h",
|
||||
"ble_v2.cc",
|
||||
"ble_v2.h",
|
||||
"bloom_filter.cc",
|
||||
"bloom_filter.h",
|
||||
"bluetooth_classic.cc",
|
||||
"bluetooth_classic.h",
|
||||
"bluetooth_radio.cc",
|
||||
"bluetooth_radio.h",
|
||||
"discovered_peripheral_callback.h",
|
||||
"discovered_peripheral_tracker.cc",
|
||||
"discovered_peripheral_tracker.h",
|
||||
"lost_entity_tracker.cc",
|
||||
"lost_entity_tracker.h",
|
||||
"mediums.cc",
|
||||
"mediums.h",
|
||||
"uuid.cc",
|
||||
"uuid.h",
|
||||
"wifi_lan.cc",
|
||||
"webrtc.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
visibility = ["//core/internal:__pkg__"],
|
||||
visibility = [
|
||||
"//core/internal:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":utils",
|
||||
"//platform:logging",
|
||||
"//platform:types",
|
||||
"//platform:utils",
|
||||
"//platform/api",
|
||||
"//platform/port:string",
|
||||
"//core:core_types",
|
||||
"//core/internal/mediums/ble_v2",
|
||||
"//core/internal/mediums/webrtc",
|
||||
"//proto/connections:offline_wire_formats_portable_proto",
|
||||
"//platform/base",
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto",
|
||||
"//absl/container:flat_hash_map",
|
||||
"//absl/container:flat_hash_set",
|
||||
"//absl/numeric:int128",
|
||||
"//absl/strings",
|
||||
"//absl/time",
|
||||
"//smhasher:libmurmur3",
|
||||
"//webrtc/api:libjingle_peerconnection_api",
|
||||
"//webrtc/api:scoped_refptr",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "utils",
|
||||
srcs = ["utils.cc"],
|
||||
hdrs = ["utils.h"],
|
||||
visibility = [
|
||||
"//core/internal:__pkg__",
|
||||
"//core/internal/mediums:__pkg__",
|
||||
"//core/internal/mediums/ble_v2:__pkg__",
|
||||
"//core/internal/mediums/webrtc:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//proto/connections:offline_wire_formats_portable_proto",
|
||||
"//platform/base",
|
||||
"//platform/public:types",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "advertisement_read_result_test",
|
||||
srcs = ["advertisement_read_result_test.cc"],
|
||||
name = "core_internal_mediums_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"ble_test.cc",
|
||||
"bloom_filter_test.cc",
|
||||
"bluetooth_classic_test.cc",
|
||||
"bluetooth_radio_test.cc",
|
||||
"lost_entity_tracker_test.cc",
|
||||
"uuid_test.cc",
|
||||
"webrtc_test.cc",
|
||||
"wifi_lan_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//core/internal/mediums/webrtc",
|
||||
"//platform/base",
|
||||
"//platform/base:test_util",
|
||||
"//platform/impl/g3", # build_cleaner: keep
|
||||
"//platform/public:comm",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//testing/base/public:gunit_main",
|
||||
"//absl/strings",
|
||||
"//absl/time",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ble_advertisement_header_test",
|
||||
srcs = ["ble_advertisement_header_test.cc"],
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform:utils",
|
||||
"//platform/impl/g3",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ble_advertisement_test",
|
||||
srcs = ["ble_advertisement_test.cc"],
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform/impl/g3",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ble_packet_test",
|
||||
srcs = ["ble_packet_test.cc"],
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform/impl/g3",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "bloom_filter_test",
|
||||
srcs = ["bloom_filter_test.cc"],
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform/impl/g3",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "lost_entity_tracker_test",
|
||||
srcs = ["lost_entity_tracker_test.cc"],
|
||||
deps = [
|
||||
":mediums",
|
||||
"//platform/api",
|
||||
"//platform/impl/g3",
|
||||
"//testing/base/public:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
add_library(core_internal_mediums STATIC)
|
||||
|
||||
target_sources(core_internal_mediums
|
||||
PRIVATE
|
||||
ble_advertisement.cc
|
||||
ble_advertisement_header.cc
|
||||
ble_packet.cc
|
||||
ble_peripheral.cc
|
||||
utils.cc
|
||||
utils.h
|
||||
PUBLIC
|
||||
advertisement_read_result.h
|
||||
ble.h
|
||||
ble_advertisement.h
|
||||
ble_advertisement_header.h
|
||||
ble_packet.h
|
||||
ble_peripheral.h
|
||||
ble_v2.h
|
||||
bloom_filter.h
|
||||
bluetooth_classic.h
|
||||
bluetooth_radio.h
|
||||
discovered_peripheral_callback.h
|
||||
discovered_peripheral_tracker.h
|
||||
lost_entity_tracker.h
|
||||
mediums.h
|
||||
uuid.h
|
||||
)
|
||||
|
||||
target_link_libraries(core_internal_mediums
|
||||
PUBLIC
|
||||
absl::numeric
|
||||
absl::strings
|
||||
platform_api
|
||||
platform_port_string
|
||||
platform_types
|
||||
platform_utils
|
||||
smhasher_murmur3
|
||||
)
|
||||
|
||||
add_executable(core_internal_mediums_test
|
||||
advertisement_read_result_test.cc
|
||||
ble_advertisement_header_test.cc
|
||||
ble_advertisement_test.cc
|
||||
ble_packet_test.cc
|
||||
bloom_filter_test.cc
|
||||
lost_entity_tracker_test.cc
|
||||
)
|
||||
|
||||
target_link_libraries(core_internal_mediums_test
|
||||
PUBLIC
|
||||
absl::time
|
||||
core_internal_mediums
|
||||
gtest
|
||||
gtest_main
|
||||
platform_impl_g3
|
||||
platform_utils
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME core_internal_mediums_test
|
||||
COMMAND core_internal_mediums_test
|
||||
)
|
||||
@@ -1,186 +0,0 @@
|
||||
#include "core/internal/mediums/advertisement_read_result.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "platform/synchronized.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename K, typename V>
|
||||
void eraseOwnedPtrFromMap(std::map<K, ConstPtr<V> >& m, const K& k) {
|
||||
typename std::map<K, ConstPtr<V> >::iterator it = m.find(k);
|
||||
if (it != m.end()) {
|
||||
it->second.destroy();
|
||||
m.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// How much to multiply the backoff duration by with every failure to read
|
||||
// from the advertisement GATT server. This should never be below 1!
|
||||
template <typename Platform>
|
||||
const float AdvertisementReadResult<Platform>::kAdvertisementBackoffMultiplier =
|
||||
2.0;
|
||||
|
||||
// The initial backoff duration when we fail to read from an advertisement
|
||||
// GATT server.
|
||||
template <typename Platform>
|
||||
const std::int64_t
|
||||
AdvertisementReadResult<Platform>::kAdvertisementBaseBackoffDurationMillis =
|
||||
1 * 1000; // 1 second
|
||||
|
||||
// The maximum backoff duration allowed between advertisement GATT server
|
||||
// reads.
|
||||
template <typename Platform>
|
||||
const std::int64_t
|
||||
AdvertisementReadResult<Platform>::kAdvertisementMaxBackoffDurationMillis =
|
||||
5 * 60 * 1000; // 5 minutes
|
||||
|
||||
template <typename Platform>
|
||||
AdvertisementReadResult<Platform>::AdvertisementReadResult()
|
||||
: lock_(Platform::createLock()),
|
||||
system_clock_(Platform::createSystemClock()),
|
||||
advertisements_(),
|
||||
backoff_duration_millis_(kAdvertisementBaseBackoffDurationMillis),
|
||||
// We need a long enough duration such that we always trigger a read
|
||||
// retry AND we always connect to it without delay. The former case
|
||||
// helps us initialize an AdvertisementReadResult so that we
|
||||
// unconditionally try reading on the first sighting. And the latter
|
||||
// case helps us connect immediately when we initialize a dummy read
|
||||
// result for fast advertisements (which don't use the GATT server).
|
||||
last_read_timestamp_millis_(system_clock_->elapsedRealtime() -
|
||||
kAdvertisementMaxBackoffDurationMillis),
|
||||
result_(Result::Value::UNKNOWN) {}
|
||||
|
||||
template <typename Platform>
|
||||
AdvertisementReadResult<Platform>::~AdvertisementReadResult() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
for (AdvertisementMap::iterator it = advertisements_.begin();
|
||||
it != advertisements_.end(); ++it) {
|
||||
it->second.destroy();
|
||||
}
|
||||
advertisements_.clear();
|
||||
}
|
||||
|
||||
// Adds a successfully read advertisement for the specified slot to this read
|
||||
// result. This is fundamentally different from
|
||||
// {@link #recordLastReadStatus(boolean)} because we can report a read
|
||||
// failure, but still manage to read some advertisements.
|
||||
// Note: advertisement should be passed in as a RefCounted Ptr. It is not the
|
||||
// responsibility of AdvertisementReadResult to make it RefCounted.
|
||||
template <typename Platform>
|
||||
void AdvertisementReadResult<Platform>::addAdvertisement(
|
||||
std::int32_t slot, /* RefCounted */ ConstPtr<ByteArray> advertisement) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_advertisement(advertisement);
|
||||
|
||||
// Blindly remove from the advertisements map to make sure any existing
|
||||
// key-value pair is destroyed.
|
||||
eraseOwnedPtrFromMap(advertisements_, slot);
|
||||
|
||||
advertisements_.insert(std::make_pair(slot, scoped_advertisement.release()));
|
||||
}
|
||||
|
||||
// Determines whether or not an advertisement was successfully read at the
|
||||
// specified slot.
|
||||
template <typename Platform>
|
||||
bool AdvertisementReadResult<Platform>::hasAdvertisement(std::int32_t slot) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return advertisements_.find(slot) != advertisements_.end();
|
||||
}
|
||||
|
||||
// Retrieves all raw advertisements that were successfully read.
|
||||
template <typename Platform>
|
||||
std::set<ConstPtr<ByteArray>>
|
||||
AdvertisementReadResult<Platform>::getAdvertisements() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
std::set<ConstPtr<ByteArray>> all_advertisements;
|
||||
for (AdvertisementMap::iterator it = advertisements_.begin();
|
||||
it != advertisements_.end(); ++it) {
|
||||
all_advertisements.insert(it->second);
|
||||
}
|
||||
|
||||
return all_advertisements;
|
||||
}
|
||||
|
||||
// Determines what stage we're in for retrying a read from an advertisement
|
||||
// GATT server.
|
||||
template <typename Platform>
|
||||
typename AdvertisementReadResult<Platform>::RetryStatus::Value
|
||||
AdvertisementReadResult<Platform>::evaluateRetryStatus() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Check if we have already succeeded reading this advertisement.
|
||||
if (result_ == Result::SUCCESS) {
|
||||
return RetryStatus::PREVIOUSLY_SUCCEEDED;
|
||||
}
|
||||
|
||||
// Check if we have recently failed to read this advertisement.
|
||||
if (getDurationSinceReadMillis() < backoff_duration_millis_) {
|
||||
return RetryStatus::TOO_SOON;
|
||||
}
|
||||
|
||||
return RetryStatus::RETRY;
|
||||
}
|
||||
|
||||
// Records the status of the latest read, and updates the next backoff
|
||||
// duration for subsequent reads. Be sure to also call
|
||||
// {@link #addAdvertisement(int, byte[])} if any advertisements were read.
|
||||
template <typename Platform>
|
||||
void AdvertisementReadResult<Platform>::recordLastReadStatus(bool is_success) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Update the last read timestamp.
|
||||
last_read_timestamp_millis_ = system_clock_->elapsedRealtime();
|
||||
|
||||
// Update the backoff duration.
|
||||
if (is_success) {
|
||||
// Reset the backoff duration now that we had a successful read.
|
||||
backoff_duration_millis_ = kAdvertisementBaseBackoffDurationMillis;
|
||||
} else {
|
||||
// Determine whether or not we were already failing before. If we were, we
|
||||
// should increase the backoff duration.
|
||||
if (result_ == Result::FAILURE) {
|
||||
// Use exponential backoff to determine the next backoff duration. This
|
||||
// simply involves multiplying our current backoff duration by some
|
||||
// multiplier.
|
||||
std::int64_t next_backoff_duration =
|
||||
kAdvertisementBackoffMultiplier * backoff_duration_millis_;
|
||||
// Update the backoff duration, making sure not to blow past the
|
||||
// ceiling.
|
||||
backoff_duration_millis_ = std::min(
|
||||
next_backoff_duration, kAdvertisementMaxBackoffDurationMillis);
|
||||
} else {
|
||||
// This is our first time failing, so we should only backoff for the
|
||||
// initial duration.
|
||||
backoff_duration_millis_ = kAdvertisementBaseBackoffDurationMillis;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the internal result.
|
||||
result_ = is_success ? Result::SUCCESS : Result::FAILURE;
|
||||
}
|
||||
|
||||
// Returns how much time has passed since we last tried reading from an
|
||||
// advertisement GATT server.
|
||||
template <typename Platform>
|
||||
std::int64_t AdvertisementReadResult<Platform>::getDurationSinceReadMillis() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return system_clock_->elapsedRealtime() - last_read_timestamp_millis_;
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,73 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_ADVERTISEMENT_READ_RESULT_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_ADVERTISEMENT_READ_RESULT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/api/system_clock.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// Representation of a GATT advertisement read result. This object helps us
|
||||
// determine whether or not we need to retry GATT reads.
|
||||
template <typename Platform>
|
||||
class AdvertisementReadResult {
|
||||
public:
|
||||
AdvertisementReadResult();
|
||||
~AdvertisementReadResult();
|
||||
|
||||
struct RetryStatus {
|
||||
enum Value {
|
||||
UNKNOWN = 0,
|
||||
RETRY = 1,
|
||||
PREVIOUSLY_SUCCEEDED = 2,
|
||||
TOO_SOON = 3,
|
||||
};
|
||||
};
|
||||
|
||||
void addAdvertisement(std::int32_t slot, ConstPtr<ByteArray> advertisement);
|
||||
bool hasAdvertisement(std::int32_t slot);
|
||||
std::set<ConstPtr<ByteArray>> getAdvertisements();
|
||||
typename RetryStatus::Value evaluateRetryStatus();
|
||||
void recordLastReadStatus(bool is_success);
|
||||
std::int64_t getDurationSinceReadMillis();
|
||||
|
||||
private:
|
||||
struct Result {
|
||||
enum Value { UNKNOWN = 0, SUCCESS = 1, FAILURE = 2 };
|
||||
};
|
||||
|
||||
static const float kAdvertisementBackoffMultiplier;
|
||||
static const std::int64_t kAdvertisementBaseBackoffDurationMillis;
|
||||
static const std::int64_t kAdvertisementMaxBackoffDurationMillis;
|
||||
|
||||
// ------------ GENERAL ------------
|
||||
ScopedPtr<Ptr<Lock>> lock_;
|
||||
ScopedPtr<Ptr<SystemClock>> system_clock_;
|
||||
|
||||
// ------ ADVERTISEMENTREADRESULT STATE ------
|
||||
// Maps slot numbers to the GATT advertisement found in that slot.
|
||||
typedef std::map<std::int32_t, /* RefCounted */ ConstPtr<ByteArray>>
|
||||
AdvertisementMap;
|
||||
AdvertisementMap advertisements_;
|
||||
|
||||
std::int64_t backoff_duration_millis_;
|
||||
std::int64_t last_read_timestamp_millis_;
|
||||
typename Result::Value result_;
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/mediums/advertisement_read_result.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_ADVERTISEMENT_READ_RESULT_H_
|
||||
@@ -1,140 +0,0 @@
|
||||
#include "core/internal/mediums/advertisement_read_result.h"
|
||||
|
||||
#include "platform/api/platform.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
using TestPlatform = platform::ImplementationPlatform;
|
||||
|
||||
constexpr char kAdvertisementBytes[] = {0x0A, 0x0B, 0x0C};
|
||||
|
||||
// Default values may be too big and impractical to wait for in the test.
|
||||
// For the test platform, we redefine them to some reasonable values.
|
||||
const absl::Duration kAdvertisementBaseBackoffDuration =
|
||||
absl::Milliseconds(1000); // 1 second
|
||||
const absl::Duration kAdvertisementMaxBackoffDuration =
|
||||
absl::Milliseconds(6000); // 6 seconds
|
||||
|
||||
template <>
|
||||
const std::int64_t AdvertisementReadResult<
|
||||
TestPlatform>::kAdvertisementMaxBackoffDurationMillis =
|
||||
ToInt64Milliseconds(kAdvertisementMaxBackoffDuration);
|
||||
template <>
|
||||
const std::int64_t
|
||||
AdvertisementReadResult<
|
||||
TestPlatform>::kAdvertisementBaseBackoffDurationMillis =
|
||||
ToInt64Milliseconds(kAdvertisementBaseBackoffDuration);
|
||||
|
||||
TEST(AdvertisementReadResultTest, AdvertisementExists) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
|
||||
|
||||
std::int32_t slot = 6;
|
||||
advertisement_read_result.addAdvertisement(
|
||||
slot,
|
||||
MakeConstPtr(new ByteArray(kAdvertisementBytes,
|
||||
sizeof(kAdvertisementBytes) / sizeof(char))));
|
||||
|
||||
ASSERT_TRUE(advertisement_read_result.hasAdvertisement(slot));
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, AdvertisementNonExistent) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
|
||||
|
||||
std::int32_t slot = 6;
|
||||
|
||||
ASSERT_FALSE(advertisement_read_result.hasAdvertisement(slot));
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, EvaluateRetryStatusInitialized) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, EvaluateRetryStatusSuccess) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<
|
||||
TestPlatform>::RetryStatus::PREVIOUSLY_SUCCEEDED);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, EvaluateRetryStatusTooSoon) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
|
||||
// Sleep for some time, but not long enough to warrant a retry.
|
||||
absl::SleepFor(absl::Milliseconds(
|
||||
absl::ToInt64Milliseconds(kAdvertisementBaseBackoffDuration) / 2));
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<TestPlatform>::RetryStatus::TOO_SOON);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, EvaluateRetryStatusRetry) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
|
||||
// Sleep long enough to warrant a retry.
|
||||
absl::SleepFor(kAdvertisementBaseBackoffDuration);
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoff) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
|
||||
// Record an additional failure so our backoff duration increases.
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
|
||||
// Sleep for the backoff duration. We shouldn't trigger a retry because the
|
||||
// backoff should have increased from failing a second time.
|
||||
absl::SleepFor(kAdvertisementBaseBackoffDuration);
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<TestPlatform>::RetryStatus::TOO_SOON);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoffMax) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
|
||||
// Record an absurd amount of failures so we hit the maximum backoff duration.
|
||||
for (std::int32_t i = 0; i < 1000; i++) {
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
|
||||
}
|
||||
|
||||
// Sleep for the maximum backoff duration. This should be enough to warrant a
|
||||
// retry.
|
||||
absl::SleepFor(kAdvertisementMaxBackoffDuration);
|
||||
|
||||
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
|
||||
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
|
||||
}
|
||||
|
||||
TEST(AdvertisementReadResultTest, GetDurationSinceRead) {
|
||||
AdvertisementReadResult<TestPlatform> advertisement_read_result;
|
||||
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
|
||||
|
||||
std::int64_t sleepTime = 420;
|
||||
absl::SleepFor(absl::Milliseconds(sleepTime));
|
||||
|
||||
ASSERT_GE(advertisement_read_result.getDurationSinceReadMillis(), sleepTime);
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
+227
-170
@@ -1,279 +1,336 @@
|
||||
#include "core/internal/mediums/ble.h"
|
||||
|
||||
#include "platform/synchronized.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "core/internal/mediums/ble_v2/ble_advertisement.h"
|
||||
#include "core/internal/mediums/utils.h"
|
||||
#include "platform/base/prng.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t BLE<Platform>::kMaxAdvertisementLength = 512;
|
||||
|
||||
template <typename Platform>
|
||||
BLE<Platform>::BLE(Ptr<BluetoothRadio<Platform>> bluetooth_radio)
|
||||
: lock_(Platform::createLock()),
|
||||
bluetooth_radio_(bluetooth_radio),
|
||||
bluetooth_adapter_(Platform::createBluetoothAdapter()),
|
||||
ble_medium_(Platform::createBLEMedium()),
|
||||
scanning_info_(),
|
||||
advertising_info_(),
|
||||
accepting_connections_info_() {}
|
||||
|
||||
template <typename Platform>
|
||||
BLE<Platform>::~BLE() {
|
||||
stopAdvertising();
|
||||
stopAcceptingConnections();
|
||||
stopScanning();
|
||||
ByteArray Ble::GenerateHash(const std::string& source, size_t size) {
|
||||
return Utils::Sha256Hash(source, size);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::isAvailable() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return !ble_medium_.isNull() && !bluetooth_adapter_.isNull();
|
||||
ByteArray Ble::GenerateDeviceToken() {
|
||||
return Utils::Sha256Hash(std::to_string(Prng().NextUint32()),
|
||||
mediums::BleAdvertisement::kDeviceTokenLength);
|
||||
}
|
||||
|
||||
// TODO(ahlee): Add fastPairData for phase 2 of C++ implementation.
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::startAdvertising(const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement) {
|
||||
Synchronized s(lock_.get());
|
||||
Ble::Ble(BluetoothRadio& radio) : radio_(radio) {}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_advertisement(advertisement);
|
||||
if (scoped_advertisement.isNull() || service_id.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start BLE advertising
|
||||
// because a null parameter was passed in.");
|
||||
bool Ble::IsAvailable() const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return IsAvailableLocked();
|
||||
}
|
||||
|
||||
bool Ble::IsAvailableLocked() const { return medium_.IsValid(); }
|
||||
|
||||
bool Ble::StartAdvertising(const std::string& service_id,
|
||||
const ByteArray& advertisement_bytes,
|
||||
const std::string& fast_advertisement_service_uuid) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (advertisement_bytes.Empty()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Refusing to turn on BLE advertising. Empty advertisement data.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scoped_advertisement->size() > kMaxAdvertisementLength) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start BLE advertising
|
||||
// because the advertisement was too long. Expected at most %d bytes but
|
||||
// received %d.", kMaxAdvertisementLength, advertisement->size());
|
||||
if (advertisement_bytes.size() > kMaxAdvertisementLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Refusing to start BLE advertising because the advertisement "
|
||||
"was too long. Expected at most %d bytes but received %d.",
|
||||
kMaxAdvertisementLength, advertisement_bytes.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAdvertising()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Failed to BLE advertise because we're
|
||||
// already advertising.");
|
||||
if (IsAdvertisingLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Failed to BLE advertise because we're already advertising.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start BLE advertising because
|
||||
// Bluetooth isn't enabled.");
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Can't start BLE scanning because Bluetooth was never turned on";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start BLE advertising because
|
||||
// BLE isn't enabled.");
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOGS(INFO) << "Can't turn on BLE advertising. BLE is not available.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ble_medium_->startAdvertising(service_id,
|
||||
scoped_advertisement.release())) {
|
||||
// TODO(ahlee) logger.atSevere().log("Failed to start BLE advertising");
|
||||
NEARBY_LOGS(INFO) << "Turning on BLE advertising with advertisement bytes="
|
||||
<< advertisement_bytes.data() << "("
|
||||
<< advertisement_bytes.size() << ")"
|
||||
<< ", service id=" << service_id
|
||||
<< ", fast advertisement service uuid="
|
||||
<< fast_advertisement_service_uuid;
|
||||
|
||||
// Wrap the connections advertisement to the medium advertisement.
|
||||
const bool fast_advertisement = !fast_advertisement_service_uuid.empty();
|
||||
ByteArray service_id_hash{GenerateHash(
|
||||
service_id, mediums::BleAdvertisement::kServiceIdHashLength)};
|
||||
ByteArray medium_advertisement_bytes{mediums::BleAdvertisement{
|
||||
mediums::BleAdvertisement::Version::kV2,
|
||||
mediums::BleAdvertisement::SocketVersion::kV2,
|
||||
fast_advertisement ? ByteArray{} : service_id_hash, advertisement_bytes,
|
||||
GenerateDeviceToken()}};
|
||||
if (medium_advertisement_bytes.Empty()) {
|
||||
NEARBY_LOGS(INFO) << "Failed to BLE advertise because we could not "
|
||||
"create a medium advertisement.";
|
||||
return false;
|
||||
}
|
||||
|
||||
advertising_info_ = MakePtr(new AdvertisingInfo(service_id));
|
||||
if (!medium_.StartAdvertising(service_id, medium_advertisement_bytes,
|
||||
fast_advertisement_service_uuid)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Failed to turn on BLE advertising with advertisement bytes="
|
||||
<< advertisement_bytes.data() << "(" << advertisement_bytes.size()
|
||||
<< ")"
|
||||
<< ", fast advertisement service uuid="
|
||||
<< fast_advertisement_service_uuid;
|
||||
return false;
|
||||
}
|
||||
|
||||
advertising_info_.Add(service_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BLE<Platform>::stopAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::StopAdvertising(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isAdvertising()) {
|
||||
// TODO(ahlee): logger.atDebug().log("Can't turn off BLE advertising because
|
||||
// it never started.");
|
||||
return;
|
||||
if (!IsAdvertisingLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO) << "Can't turn off BLE advertising; it is already off";
|
||||
return false;
|
||||
}
|
||||
|
||||
ble_medium_->stopAdvertising(advertising_info_->service_id);
|
||||
NEARBY_LOGS(INFO) << "Turned off BLE advertising with service id="
|
||||
<< service_id;
|
||||
bool ret = medium_.StopAdvertising(service_id);
|
||||
// Reset our bundle of advertising state to mark that we're no longer
|
||||
// advertising.
|
||||
advertising_info_.destroy();
|
||||
|
||||
// TODO(ahlee): logger.atVerbose().log("Turned BLE advertising off");
|
||||
advertising_info_.Remove(service_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::isAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsAdvertising(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !advertising_info_.isNull();
|
||||
return IsAdvertisingLocked(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::startScanning(
|
||||
const string& service_id,
|
||||
Ptr<DiscoveredPeripheralCallback> discovered_peripheral_callback) {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsAdvertisingLocked(const std::string& service_id) {
|
||||
return advertising_info_.Existed(service_id);
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<DiscoveredPeripheralCallback>>
|
||||
scoped_discovered_peripheral_callback(discovered_peripheral_callback);
|
||||
if (scoped_discovered_peripheral_callback.isNull() || service_id.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start BLE scanning
|
||||
// because a null parameter was passed in.");
|
||||
bool Ble::StartScanning(const std::string& service_id,
|
||||
const std::string& fast_advertisement_service_uuid,
|
||||
DiscoveredPeripheralCallback callback) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
discovered_peripheral_callback_ = std::move(callback);
|
||||
|
||||
if (service_id.empty()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Refusing to start BLE scanning with empty service id.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isScanning()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start BLE scanning
|
||||
// because we are already scanning.");
|
||||
if (IsScanningLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO) << "Refusing to start scan of BLE peripherals because "
|
||||
"another scanning is already in-progress.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start BLE scanning because
|
||||
// Bluetooth was never turned on");
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Can't start BLE scanning because Bluetooth was never turned on";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start BLE scanning because
|
||||
// BLE isn't available.");
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Can't scan BLE peripherals because BLE isn't available.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<BLEDiscoveredPeripheralCallback>>
|
||||
scoped_ble_discovered_peripheral_callback(
|
||||
new BLEDiscoveredPeripheralCallback(
|
||||
scoped_discovered_peripheral_callback.release()));
|
||||
if (!ble_medium_->startScanning(
|
||||
service_id, scoped_ble_discovered_peripheral_callback.get())) {
|
||||
// TODO(ahlee): logger.atSevere().log("Failed to start BLE scanning.");
|
||||
if (!medium_.StartScanning(
|
||||
service_id, fast_advertisement_service_uuid,
|
||||
{
|
||||
.peripheral_discovered_cb =
|
||||
[this](BlePeripheral& peripheral,
|
||||
const std::string& service_id,
|
||||
const ByteArray& medium_advertisement_bytes,
|
||||
bool fast_advertisement) {
|
||||
// Unwrap connection BleAdvertisement from medium
|
||||
// BleAdvertisement.
|
||||
auto connection_advertisement_bytes =
|
||||
UnwrapAdvertisementBytes(medium_advertisement_bytes);
|
||||
discovered_peripheral_callback_.peripheral_discovered_cb(
|
||||
peripheral, service_id, connection_advertisement_bytes,
|
||||
fast_advertisement);
|
||||
},
|
||||
.peripheral_lost_cb =
|
||||
[this](BlePeripheral& peripheral,
|
||||
const std::string& service_id) {
|
||||
discovered_peripheral_callback_.peripheral_lost_cb(
|
||||
peripheral, service_id);
|
||||
},
|
||||
})) {
|
||||
NEARBY_LOGS(INFO) << "Failed to start scan of BLE services.";
|
||||
return false;
|
||||
}
|
||||
|
||||
scanning_info_ = MakePtr(new ScanningInfo(
|
||||
service_id, scoped_ble_discovered_peripheral_callback.release()));
|
||||
NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id;
|
||||
// Mark the fact that we're currently performing a BLE discovering.
|
||||
scanning_info_.Add(service_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BLE<Platform>::stopScanning() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::StopScanning(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isScanning()) {
|
||||
// TODO(ahlee): logger.atDebug().log("Can't turn off BLE scanning because we
|
||||
// never started scanning.");
|
||||
return;
|
||||
if (!IsScanningLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO) << "Can't turn off BLE sacanning because we never "
|
||||
"started scanning.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ble_medium_->stopScanning(scanning_info_->service_id);
|
||||
// Reset our bundle of scanning state to mark that we're no longer scanning.
|
||||
scanning_info_.destroy();
|
||||
NEARBY_LOG(INFO, "Turned off BLE scanning with service id=%s",
|
||||
service_id.c_str());
|
||||
bool ret = medium_.StopScanning(service_id);
|
||||
scanning_info_.Clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::isScanning() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsScanning(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !scanning_info_.isNull();
|
||||
return IsScanningLocked(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::startAcceptingConnections(
|
||||
const string& service_id,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback) {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsScanningLocked(const std::string& service_id) {
|
||||
return scanning_info_.Existed(service_id);
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<AcceptedConnectionCallback>>
|
||||
scoped_accepted_connection_callback(accepted_connection_callback);
|
||||
if (scoped_accepted_connection_callback.isNull() || service_id.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start accepting BLE
|
||||
// connections because a null parameter was passed in.");
|
||||
bool Ble::StartAcceptingConnections(const std::string& service_id,
|
||||
AcceptedConnectionCallback callback) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (service_id.empty()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Refusing to start accepting BLE connections with empty service id.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAcceptingConnections()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to start accepting BLE
|
||||
// connections for %s because another BLE server socket is already
|
||||
// in-progress.", service_id);
|
||||
if (IsAcceptingConnectionsLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Refusing to start accepting BLE connections for "
|
||||
<< service_id
|
||||
<< " because another BLE peripheral socket is already in-progress.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start accepting BLE connections
|
||||
// for %s because Bluetooth isn't enabled.", serviceId);
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for "
|
||||
<< service_id
|
||||
<< " because Bluetooth isn't enabled.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't start accepting BLE connections
|
||||
// for %s because BLE isn't available.", serviceId);
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for "
|
||||
<< service_id << " because BLE isn't available.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<BLEAcceptedConnectionCallback>>
|
||||
scoped_ble_accepted_connection_callback(new BLEAcceptedConnectionCallback(
|
||||
scoped_accepted_connection_callback.release()));
|
||||
if (!ble_medium_->startAcceptingConnections(
|
||||
service_id, scoped_ble_accepted_connection_callback.get())) {
|
||||
if (!medium_.StartAcceptingConnections(service_id, callback)) {
|
||||
NEARBY_LOGS(INFO) << "Failed to accept connections callback for "
|
||||
<< service_id << " .";
|
||||
return false;
|
||||
}
|
||||
|
||||
accepting_connections_info_ = MakePtr(new AcceptingConnectionsInfo(
|
||||
service_id, scoped_ble_accepted_connection_callback.release()));
|
||||
accepting_connections_info_.Add(service_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BLE<Platform>::stopAcceptingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::StopAcceptingConnections(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isAcceptingConnections()) {
|
||||
// TODO(ahlee): logger.atDebug().log("Can't stop accepting BLE connections
|
||||
// because it was never started.");
|
||||
return;
|
||||
if (!IsAcceptingConnectionsLocked(service_id)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Can't stop accepting BLE connections because it was never started.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ble_medium_->stopAcceptingConnections(
|
||||
accepting_connections_info_->service_id);
|
||||
bool ret = medium_.StopAcceptingConnections(service_id);
|
||||
// Reset our bundle of accepting connections state to mark that we're no
|
||||
// longer accepting connections.
|
||||
accepting_connections_info_.destroy();
|
||||
accepting_connections_info_.Remove(service_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLE<Platform>::isAcceptingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsAcceptingConnections(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !accepting_connections_info_.isNull();
|
||||
return IsAcceptingConnectionsLocked(service_id);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<BLESocket> BLE<Platform>::connect(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id) {
|
||||
Synchronized s(lock_.get());
|
||||
bool Ble::IsAcceptingConnectionsLocked(const std::string& service_id) {
|
||||
return accepting_connections_info_.Existed(service_id);
|
||||
}
|
||||
|
||||
if (ble_peripheral.isNull() || service_id.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to create client BLE socket
|
||||
// because at least one of blePeripheral or serviceId is null.");
|
||||
return Ptr<BLESocket>();
|
||||
BleSocket Ble::Connect(BlePeripheral& peripheral,
|
||||
const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOGS(INFO) << "BLE::Connect: service=" << &peripheral;
|
||||
// Socket to return. To allow for NRVO to work, it has to be a single object.
|
||||
BleSocket socket;
|
||||
|
||||
if (service_id.empty()) {
|
||||
NEARBY_LOGS(INFO) << "Refusing to create BLE socket with empty service_id.";
|
||||
return socket;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't create client BLE socket to %s
|
||||
// because Bluetooth isn't enabled.", blePeripheral);
|
||||
return Ptr<BLESocket>();
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOGS(INFO) << "Can't create client BLE socket to "
|
||||
<< &peripheral << " because Bluetooth isn't enabled.";
|
||||
return socket;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Can't create client BLE socket to %s
|
||||
// because BLE isn't available.", blePeripheral);
|
||||
return Ptr<BLESocket>();
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOGS(INFO) << "Can't create client BLE socket [service_id="
|
||||
<< service_id << "]; BLE isn't available.";
|
||||
return socket;
|
||||
}
|
||||
|
||||
return ble_medium_->connect(ble_peripheral, service_id);
|
||||
socket = medium_.Connect(peripheral, service_id);
|
||||
if (!socket.IsValid()) {
|
||||
NEARBY_LOGS(INFO) << "Failed to Connect via BLE [service=" << service_id
|
||||
<< "]";
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
ByteArray Ble::UnwrapAdvertisementBytes(
|
||||
const ByteArray& medium_advertisement_data) {
|
||||
mediums::BleAdvertisement medium_ble_advertisement{medium_advertisement_data};
|
||||
if (!medium_ble_advertisement.IsValid()) {
|
||||
return ByteArray{};
|
||||
}
|
||||
|
||||
return medium_ble_advertisement.GetData();
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
+121
-146
@@ -2,196 +2,171 @@
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/mediums/bluetooth_radio.h"
|
||||
#include "platform/api/ble.h"
|
||||
#include "platform/api/bluetooth_adapter.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "core/listeners.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/ble.h"
|
||||
#include "platform/public/multi_thread_executor.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
class BLE {
|
||||
class Ble {
|
||||
public:
|
||||
explicit BLE(Ptr<BluetoothRadio<Platform>> bluetooth_radio);
|
||||
~BLE();
|
||||
using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback;
|
||||
using AcceptedConnectionCallback = BleMedium::AcceptedConnectionCallback;
|
||||
|
||||
bool isAvailable();
|
||||
explicit Ble(BluetoothRadio& bluetooth_radio);
|
||||
~Ble() = default;
|
||||
|
||||
bool startAdvertising(const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement);
|
||||
void stopAdvertising();
|
||||
// Returns true, if Ble communications are supported by a platform.
|
||||
bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
class DiscoveredPeripheralCallback {
|
||||
public:
|
||||
virtual ~DiscoveredPeripheralCallback() {}
|
||||
// Sets custom advertisement data, and then enables Ble advertising.
|
||||
// Returns true, if data is successfully set, and false otherwise.
|
||||
bool StartAdvertising(const std::string& service_id,
|
||||
const ByteArray& advertisement_bytes,
|
||||
const std::string& fast_advertisement_service_uuid)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
virtual void onPeripheralDiscovered(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement) = 0;
|
||||
virtual void onPeripheralLost(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id) = 0;
|
||||
};
|
||||
// Disables Ble advertising.
|
||||
bool StopAdvertising(const std::string& service_id)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool startScanning(
|
||||
const string& service_id,
|
||||
Ptr<DiscoveredPeripheralCallback> discovered_peripheral_callback);
|
||||
void stopScanning();
|
||||
bool IsAdvertising(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
class AcceptedConnectionCallback {
|
||||
public:
|
||||
virtual ~AcceptedConnectionCallback() {}
|
||||
// Enables Ble scanning mode. Will report any discoverable peripherals in
|
||||
// range through a callback. Returns true, if scanning mode was enabled,
|
||||
// false otherwise.
|
||||
bool StartScanning(const std::string& service_id,
|
||||
const std::string& fast_advertisement_service_uuid,
|
||||
DiscoveredPeripheralCallback callback)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
virtual void onConnectionAccepted(Ptr<BLESocket> socket,
|
||||
const string& service_id) = 0;
|
||||
};
|
||||
// Disables Ble discovery mode.
|
||||
bool StopScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool startAcceptingConnections(
|
||||
const string& service_id,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback);
|
||||
void stopAcceptingConnections();
|
||||
bool isAcceptingConnections();
|
||||
bool IsScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
Ptr<BLESocket> connect(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id);
|
||||
// Starts a worker thread, creates a Ble socket, associates it with a
|
||||
// service id.
|
||||
bool StartAcceptingConnections(const std::string& service_id,
|
||||
AcceptedConnectionCallback callback)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Closes socket corresponding to a service id.
|
||||
bool StopAcceptingConnections(const std::string& service_id)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool IsAcceptingConnections(const std::string& service_id)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if this object owns a valid platform implementation.
|
||||
bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock lock(&mutex_);
|
||||
return medium_.IsValid();
|
||||
}
|
||||
|
||||
// Returns true if this object has a valid BluetoothAdapter reference.
|
||||
bool IsAdapterValid() const ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock lock(&mutex_);
|
||||
return adapter_.IsValid();
|
||||
}
|
||||
|
||||
// Establishes connection to Ble peripheral that was might be started on
|
||||
// another peripheral with StartAcceptingConnections() using the same
|
||||
// service_id. Blocks until connection is established, or server-side is
|
||||
// terminated. Returns socket instance. On success, BleSocket.IsValid() return
|
||||
// true.
|
||||
BleSocket Connect(BlePeripheral& peripheral, const std::string& service_id)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
// TODO(ahlee): Rename to DiscoveredPeripheralCallbackBridge
|
||||
class BLEDiscoveredPeripheralCallback
|
||||
: public BLEMedium::DiscoveredPeripheralCallback {
|
||||
public:
|
||||
explicit BLEDiscoveredPeripheralCallback(
|
||||
Ptr<BLE::DiscoveredPeripheralCallback> discovered_peripheral_callback)
|
||||
: discovered_peripheral_callback_(discovered_peripheral_callback) {}
|
||||
~BLEDiscoveredPeripheralCallback() override {
|
||||
// Nothing to do.
|
||||
struct AdvertisingInfo {
|
||||
bool Empty() const { return service_ids.empty(); }
|
||||
void Clear() { service_ids.clear(); }
|
||||
void Add(const std::string& service_id) { service_ids.emplace(service_id); }
|
||||
void Remove(const std::string& service_id) {
|
||||
service_ids.erase(service_id);
|
||||
}
|
||||
bool Existed(const std::string& service_id) const {
|
||||
return service_ids.contains(service_id);
|
||||
}
|
||||
|
||||
void onPeripheralDiscovered(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement) override {
|
||||
discovered_peripheral_callback_->onPeripheralDiscovered(
|
||||
ble_peripheral, service_id, advertisement);
|
||||
}
|
||||
void onPeripheralLost(Ptr<BLEPeripheral> ble_peripheral,
|
||||
const string& service_id) override {
|
||||
discovered_peripheral_callback_->onPeripheralLost(ble_peripheral,
|
||||
service_id);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<BLE::DiscoveredPeripheralCallback>>
|
||||
discovered_peripheral_callback_;
|
||||
};
|
||||
|
||||
// TODO(ahlee): Rename to AcceptedConnectionCallbackBridge
|
||||
class BLEAcceptedConnectionCallback
|
||||
: public BLEMedium::AcceptedConnectionCallback {
|
||||
public:
|
||||
explicit BLEAcceptedConnectionCallback(
|
||||
Ptr<BLE::AcceptedConnectionCallback> accepted_connection_callback)
|
||||
: accepted_connection_callback_(accepted_connection_callback) {}
|
||||
~BLEAcceptedConnectionCallback() override {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
void onConnectionAccepted(Ptr<BLESocket> ble_socket,
|
||||
const string& service_id) override {
|
||||
accepted_connection_callback_->onConnectionAccepted(ble_socket,
|
||||
service_id);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<Ptr<BLE::AcceptedConnectionCallback>>
|
||||
accepted_connection_callback_;
|
||||
absl::flat_hash_set<std::string> service_ids;
|
||||
};
|
||||
|
||||
struct ScanningInfo {
|
||||
ScanningInfo(
|
||||
const string& service_id,
|
||||
Ptr<BLEDiscoveredPeripheralCallback> ble_discovered_peripheral_callback)
|
||||
: service_id(service_id),
|
||||
ble_discovered_peripheral_callback(
|
||||
ble_discovered_peripheral_callback) {}
|
||||
~ScanningInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
bool Empty() const { return service_ids.empty(); }
|
||||
void Clear() { service_ids.clear(); }
|
||||
void Add(const std::string& service_id) { service_ids.emplace(service_id); }
|
||||
void Remove(const std::string& service_id) {
|
||||
service_ids.erase(service_id);
|
||||
}
|
||||
bool Existed(const std::string& service_id) const {
|
||||
return service_ids.contains(service_id);
|
||||
}
|
||||
|
||||
const string service_id;
|
||||
ScopedPtr<Ptr<BLEDiscoveredPeripheralCallback>>
|
||||
ble_discovered_peripheral_callback;
|
||||
};
|
||||
|
||||
struct AdvertisingInfo {
|
||||
explicit AdvertisingInfo(const string& service_id)
|
||||
: service_id(service_id) {}
|
||||
~AdvertisingInfo() {}
|
||||
|
||||
const string service_id;
|
||||
absl::flat_hash_set<std::string> service_ids;
|
||||
};
|
||||
|
||||
struct AcceptingConnectionsInfo {
|
||||
AcceptingConnectionsInfo(
|
||||
const string& service_id,
|
||||
Ptr<BLEAcceptedConnectionCallback> ble_accepted_connection_callback)
|
||||
: service_id(service_id),
|
||||
ble_accepted_connection_callback(ble_accepted_connection_callback) {}
|
||||
~AcceptingConnectionsInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
bool Empty() const { return service_ids.empty(); }
|
||||
void Clear() { service_ids.clear(); }
|
||||
void Add(const std::string& service_id) { service_ids.emplace(service_id); }
|
||||
void Remove(const std::string& service_id) {
|
||||
service_ids.erase(service_id);
|
||||
}
|
||||
bool Existed(const std::string& service_id) const {
|
||||
return service_ids.contains(service_id);
|
||||
}
|
||||
|
||||
const string service_id;
|
||||
ScopedPtr<Ptr<BLEAcceptedConnectionCallback>>
|
||||
ble_accepted_connection_callback;
|
||||
absl::flat_hash_set<std::string> service_ids;
|
||||
};
|
||||
|
||||
static const std::int32_t kMaxAdvertisementLength;
|
||||
static constexpr int kMaxAdvertisementLength = 512;
|
||||
|
||||
bool isAdvertising();
|
||||
bool isScanning();
|
||||
static ByteArray GenerateHash(const std::string& source, size_t size);
|
||||
static ByteArray GenerateDeviceToken();
|
||||
|
||||
// ------------ GENERAL ------------
|
||||
// Same as IsAvailable(), but must be called with mutex_ held.
|
||||
bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
ScopedPtr<Ptr<Lock>> lock_;
|
||||
// Same as IsAdvertising(), but must be called with mutex_ held.
|
||||
bool IsAdvertisingLocked(const std::string& service_id)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// ------------ CORE BLE ------------
|
||||
// Same as IsDiscovering(), but must be called with mutex_ held.
|
||||
bool IsScanningLocked(const std::string& service_id)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Ptr<BluetoothRadio<Platform>> bluetooth_radio_;
|
||||
ScopedPtr<Ptr<BluetoothAdapter>> bluetooth_adapter_;
|
||||
// The underlying, per-platform implementation.
|
||||
ScopedPtr<Ptr<BLEMedium>> ble_medium_;
|
||||
// Same as IsAcceptingConnections(), but must be called with mutex_ held.
|
||||
bool IsAcceptingConnectionsLocked(const std::string& service_id)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// ------------ DISCOVERY ------------
|
||||
// Extract connection advertisement from medium advertisement.
|
||||
ByteArray UnwrapAdvertisementBytes(
|
||||
const ByteArray& medium_advertisement_data);
|
||||
|
||||
// A bundle of state required to start/stop BLE scanning. When non-null,
|
||||
// we are currently performing a BLE scan.
|
||||
// In the Java code this maps to the bleListener and
|
||||
// bleScanningMediumOperation.
|
||||
Ptr<ScanningInfo> scanning_info_;
|
||||
|
||||
// ------------ ADVERTISING ------------
|
||||
|
||||
// A bundle of state required to start/stop BLE advertising. When non-null,
|
||||
// we are currently advertising over BLE.
|
||||
// In the Java code this maps to bleAdvertiser, advertiseCallback, and
|
||||
// bleAdvertisingMediumOperation.
|
||||
Ptr<AdvertisingInfo> advertising_info_;
|
||||
|
||||
// A bundle of state required to start/stop accepting BLE connections. When
|
||||
// non-null, we are currently accepting BLE connections.
|
||||
// In the Java code this maps to the bleServerSocket.
|
||||
Ptr<AcceptingConnectionsInfo> accepting_connections_info_;
|
||||
mutable Mutex mutex_;
|
||||
BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_);
|
||||
BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){
|
||||
radio_.GetBluetoothAdapter()};
|
||||
BleMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_};
|
||||
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
|
||||
ScanningInfo scanning_info_ ABSL_GUARDED_BY(mutex_);
|
||||
DiscoveredPeripheralCallback discovered_peripheral_callback_;
|
||||
AcceptingConnectionsInfo accepting_connections_info_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/mediums/ble.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_H_
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
#include "core/internal/mediums/ble_advertisement.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "platform/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
const std::uint32_t BLEAdvertisement::kServiceIdHashLength = 3;
|
||||
|
||||
const std::uint32_t BLEAdvertisement::kVersionLength = 1;
|
||||
// Length of one int. Be sure to re-evaluate how we compute data size in this
|
||||
// class if this constant ever changes!
|
||||
const std::uint32_t BLEAdvertisement::kDataSizeLength = 4;
|
||||
const std::uint32_t BLEAdvertisement::kMinAdvertisementLength =
|
||||
kVersionLength + kServiceIdHashLength + kDataSizeLength;
|
||||
// The maximum length for a GATT characteristic value is 512 bytes, so make sure
|
||||
// the entire advertisement is less than that. The data can take up whatever
|
||||
// space is remaining after the bytes preceding it.
|
||||
const std::uint32_t BLEAdvertisement::kMaxDataSize =
|
||||
512 - kMinAdvertisementLength;
|
||||
const std::uint16_t BLEAdvertisement::kVersionBitmask = 0x0E0;
|
||||
const std::uint16_t BLEAdvertisement::kSocketVersionBitmask = 0x01C;
|
||||
|
||||
ConstPtr<BLEAdvertisement> BLEAdvertisement::fromBytes(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes) {
|
||||
if (ble_advertisement_bytes.isNull()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisement: null bytes passed in");
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
if (ble_advertisement_bytes->size() < kMinAdvertisementLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisement: expecting min %u raw "
|
||||
"bytes, got %zu",
|
||||
kMinAdvertisementLength, ble_advertisement_bytes->size());
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
// Now, time to read the bytes!
|
||||
const char *ble_advertisement_bytes_read_ptr =
|
||||
ble_advertisement_bytes->getData();
|
||||
|
||||
// 1. Version.
|
||||
Version::Value version = parseVersionFromByte(
|
||||
static_cast<std::uint16_t>(*ble_advertisement_bytes_read_ptr));
|
||||
if (!isSupportedVersion(version)) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisement: unsupported Version %u",
|
||||
version);
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
// 2. Socket Version.
|
||||
SocketVersion::Value socket_version = parseSocketVersionFromByte(
|
||||
static_cast<std::uint16_t>(*ble_advertisement_bytes_read_ptr));
|
||||
if (!isSupportedSocketVersion(socket_version)) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Cannot deserialize BLEAdvertisement: unsupported SocketVersion %u",
|
||||
socket_version);
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
ble_advertisement_bytes_read_ptr += kVersionLength;
|
||||
|
||||
// 3. Service ID hash.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(ble_advertisement_bytes_read_ptr, kServiceIdHashLength)));
|
||||
ble_advertisement_bytes_read_ptr += kServiceIdHashLength;
|
||||
|
||||
// 4.1. Data size.
|
||||
size_t expected_data_size =
|
||||
deserializeDataSize(ble_advertisement_bytes_read_ptr);
|
||||
if (expected_data_size < 0) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisement: negative data size %zu",
|
||||
expected_data_size);
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
ble_advertisement_bytes_read_ptr += kDataSizeLength;
|
||||
|
||||
// Check that the stated data size is the same as what we received.
|
||||
size_t actual_data_size = computeDataSize(ble_advertisement_bytes);
|
||||
if (actual_data_size < expected_data_size) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisement: expected data to be %zu "
|
||||
"bytes, got %zu bytes",
|
||||
expected_data_size, actual_data_size);
|
||||
return ConstPtr<BLEAdvertisement>();
|
||||
}
|
||||
|
||||
// 4.2. Data.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(MakeConstPtr(
|
||||
new ByteArray(ble_advertisement_bytes_read_ptr, expected_data_size)));
|
||||
ble_advertisement_bytes_read_ptr += expected_data_size;
|
||||
|
||||
return MakeRefCountedConstPtr(new BLEAdvertisement(
|
||||
version, socket_version, scoped_service_id_hash.release(),
|
||||
scoped_data.release()));
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisement::toBytes(
|
||||
Version::Value version, SocketVersion::Value socket_version,
|
||||
ConstPtr<ByteArray> service_id_hash, ConstPtr<ByteArray> data) {
|
||||
// Check that the given input is valid.
|
||||
if (!isSupportedVersion(version)) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEAdvertisement: unsupported Version %u",
|
||||
version);
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
if (!isSupportedSocketVersion(socket_version)) {
|
||||
NEARBY_LOG(
|
||||
INFO, "Cannot serialize BLEAdvertisement: unsupported SocketVersion %u",
|
||||
socket_version);
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
if (service_id_hash->size() != kServiceIdHashLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEAdvertisement: expected a service_id_hash "
|
||||
"of %u bytes, but got %zu",
|
||||
kServiceIdHashLength, service_id_hash->size());
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
if (data->size() > kMaxDataSize) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEAdvertisement: expected data of at most %u "
|
||||
"bytes, but got %zu",
|
||||
kMaxDataSize, data->size());
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
// Initialize the bytes.
|
||||
size_t advertisement_length = computeAdvertisementLength(data);
|
||||
Ptr<ByteArray> advertisement_bytes{new ByteArray{advertisement_length}};
|
||||
char *advertisement_bytes_write_ptr = advertisement_bytes->getData();
|
||||
|
||||
// 1. Version.
|
||||
serializeVersionByte(advertisement_bytes_write_ptr, version);
|
||||
|
||||
// 2. SocketVersion.
|
||||
serializeSocketVersionByte(advertisement_bytes_write_ptr, socket_version);
|
||||
advertisement_bytes_write_ptr += kVersionLength;
|
||||
|
||||
// 3. Service ID hash.
|
||||
memcpy(advertisement_bytes_write_ptr, service_id_hash->getData(),
|
||||
kServiceIdHashLength);
|
||||
advertisement_bytes_write_ptr += kServiceIdHashLength;
|
||||
|
||||
// 4.1. Data length.
|
||||
serializeDataSize(advertisement_bytes_write_ptr, data->size());
|
||||
advertisement_bytes_write_ptr += kDataSizeLength;
|
||||
|
||||
// 4.2. Data.
|
||||
memcpy(advertisement_bytes_write_ptr, data->getData(), data->size());
|
||||
advertisement_bytes_write_ptr += data->size();
|
||||
|
||||
return ConstifyPtr(advertisement_bytes);
|
||||
}
|
||||
|
||||
bool BLEAdvertisement::isSupportedVersion(Version::Value version) {
|
||||
return version >= Version::V1 && version <= Version::V2;
|
||||
}
|
||||
|
||||
bool BLEAdvertisement::isSupportedSocketVersion(
|
||||
SocketVersion::Value socket_version) {
|
||||
return socket_version >= SocketVersion::V1 &&
|
||||
socket_version <= SocketVersion::V2;
|
||||
}
|
||||
|
||||
BLEAdvertisement::Version::Value BLEAdvertisement::parseVersionFromByte(
|
||||
std::uint16_t byte) {
|
||||
return static_cast<BLEAdvertisement::Version::Value>(
|
||||
(byte & kVersionBitmask) >> 5);
|
||||
}
|
||||
|
||||
BLEAdvertisement::SocketVersion::Value
|
||||
BLEAdvertisement::parseSocketVersionFromByte(std::uint16_t byte) {
|
||||
return static_cast<SocketVersion::Value>((byte & kSocketVersionBitmask) >> 2);
|
||||
}
|
||||
|
||||
size_t BLEAdvertisement::deserializeDataSize(
|
||||
const char *data_size_bytes_read_ptr) {
|
||||
// Allocate a chunk of memory to store our deserialized size.
|
||||
char data_size_bytes[kDataSizeLength];
|
||||
|
||||
// Assign the bits of our size from the given raw bytes, keeping in mind that
|
||||
// we need to convert from Big Endian to Little Endian in the process.
|
||||
for (int i = 0; i < kDataSizeLength; ++i) {
|
||||
data_size_bytes[i] = data_size_bytes_read_ptr[kDataSizeLength - i - 1];
|
||||
}
|
||||
|
||||
// Interpret the char array as a single int.
|
||||
return static_cast<size_t>(
|
||||
*(reinterpret_cast<std::uint32_t *>(&data_size_bytes)));
|
||||
}
|
||||
|
||||
size_t BLEAdvertisement::computeDataSize(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes) {
|
||||
return ble_advertisement_bytes->size() - kMinAdvertisementLength;
|
||||
}
|
||||
|
||||
size_t BLEAdvertisement::computeAdvertisementLength(ConstPtr<ByteArray> data) {
|
||||
// The advertisement length is the minimum length + the length of the data.
|
||||
return kMinAdvertisementLength + data->size();
|
||||
}
|
||||
|
||||
void BLEAdvertisement::serializeVersionByte(char *version_byte_write_ptr,
|
||||
Version::Value version) {
|
||||
*version_byte_write_ptr |=
|
||||
static_cast<char>((version << 5) & kVersionBitmask);
|
||||
}
|
||||
|
||||
void BLEAdvertisement::serializeSocketVersionByte(
|
||||
char *socket_version_byte_write_ptr, SocketVersion::Value socket_version) {
|
||||
*socket_version_byte_write_ptr |=
|
||||
static_cast<char>((socket_version << 2) & kSocketVersionBitmask);
|
||||
}
|
||||
|
||||
void BLEAdvertisement::serializeDataSize(char *data_size_bytes_write_ptr,
|
||||
size_t data_size) {
|
||||
// Get a raw representation of the data size bytes in memory.
|
||||
char *data_size_bytes = reinterpret_cast<char *>(&data_size);
|
||||
|
||||
// Append these raw bytes to advertisement bytes, keeping in mind that we need
|
||||
// to convert from Little Endian to Big Endian in the process.
|
||||
for (int i = 0; i < kDataSizeLength; ++i) {
|
||||
data_size_bytes_write_ptr[i] = data_size_bytes[kDataSizeLength - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
BLEAdvertisement::BLEAdvertisement(Version::Value version,
|
||||
SocketVersion::Value socket_version,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data)
|
||||
: version_(version),
|
||||
socket_version_(socket_version),
|
||||
service_id_hash_(service_id_hash),
|
||||
data_(data) {}
|
||||
|
||||
BLEAdvertisement::~BLEAdvertisement() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
BLEAdvertisement::Version::Value BLEAdvertisement::getVersion() const {
|
||||
return version_;
|
||||
}
|
||||
|
||||
BLEAdvertisement::SocketVersion::Value BLEAdvertisement::getSocketVersion()
|
||||
const {
|
||||
return socket_version_;
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisement::getServiceIdHash() const {
|
||||
return service_id_hash_.get();
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisement::getData() const { return data_.get(); }
|
||||
|
||||
bool BLEAdvertisement::operator==(const BLEAdvertisement &rhs) const {
|
||||
return this->getVersion() == rhs.getVersion() &&
|
||||
this->getSocketVersion() == rhs.getSocketVersion() &&
|
||||
*(this->getServiceIdHash()) == *(rhs.getServiceIdHash()) &&
|
||||
*(this->getData()) == *(rhs.getData());
|
||||
}
|
||||
|
||||
bool BLEAdvertisement::operator<(const BLEAdvertisement &rhs) const {
|
||||
if (this->getVersion() != rhs.getVersion()) {
|
||||
return this->getVersion() < rhs.getVersion();
|
||||
}
|
||||
if (this->getSocketVersion() != rhs.getSocketVersion()) {
|
||||
return this->getSocketVersion() < rhs.getSocketVersion();
|
||||
}
|
||||
if (*(this->getServiceIdHash()) != *(rhs.getServiceIdHash())) {
|
||||
return *(this->getServiceIdHash()) < *(rhs.getServiceIdHash());
|
||||
}
|
||||
return *(this->getData()) < *(rhs.getData());
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,100 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// Represents the format of the Mediums BLE Advertisement used in advertising
|
||||
// and discovery.
|
||||
//
|
||||
// [VERSION][SOCKET_VERSION][2_RESERVED_BITS][SERVICE_ID_HASH][DATA_SIZE][DATA]
|
||||
//
|
||||
// See go/nearby-ble-design for more information.
|
||||
class BLEAdvertisement {
|
||||
public:
|
||||
// Versions of the BLEAdvertisement.
|
||||
struct Version {
|
||||
enum Value {
|
||||
UNKNOWN = 0,
|
||||
V1 = 1,
|
||||
V2 = 2,
|
||||
// Version is only allocated 3 bits in the BLEAdvertisement, so this can
|
||||
// never go beyond V7.
|
||||
};
|
||||
};
|
||||
|
||||
// Versions of the BLESocket.
|
||||
struct SocketVersion {
|
||||
enum Value {
|
||||
UNKNOWN = 0,
|
||||
V1 = 1,
|
||||
V2 = 2,
|
||||
// SocketVersion is only allocated 3 bits in the BLEAdvertisement, so this
|
||||
// can never go beyond V7.
|
||||
};
|
||||
};
|
||||
|
||||
static ConstPtr<BLEAdvertisement> fromBytes(
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes);
|
||||
|
||||
static ConstPtr<ByteArray> toBytes(Version::Value version,
|
||||
SocketVersion::Value socket_version,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data);
|
||||
|
||||
static const std::uint32_t kServiceIdHashLength;
|
||||
|
||||
~BLEAdvertisement();
|
||||
|
||||
Version::Value getVersion() const;
|
||||
SocketVersion::Value getSocketVersion() const;
|
||||
ConstPtr<ByteArray> getServiceIdHash() const;
|
||||
ConstPtr<ByteArray> getData() const;
|
||||
|
||||
// Operator overloads when comparing ConstPtr<BLEAdvertisement>.
|
||||
bool operator==(const BLEAdvertisement &rhs) const;
|
||||
bool operator<(const BLEAdvertisement &rhs) const;
|
||||
|
||||
private:
|
||||
static bool isSupportedVersion(Version::Value version);
|
||||
static bool isSupportedSocketVersion(SocketVersion::Value socket_version);
|
||||
static Version::Value parseVersionFromByte(std::uint16_t byte);
|
||||
static SocketVersion::Value parseSocketVersionFromByte(std::uint16_t byte);
|
||||
static size_t deserializeDataSize(const char *data_size_bytes_read_ptr);
|
||||
static size_t computeDataSize(ConstPtr<ByteArray> ble_advertisement_bytes);
|
||||
static size_t computeAdvertisementLength(ConstPtr<ByteArray> data);
|
||||
static void serializeVersionByte(char *version_byte_write_ptr,
|
||||
Version::Value version);
|
||||
static void serializeSocketVersionByte(char *socket_version_byte_write_ptr,
|
||||
SocketVersion::Value socket_version);
|
||||
static void serializeDataSize(char *data_size_bytes_write_ptr,
|
||||
size_t data_size);
|
||||
|
||||
static const std::uint32_t kVersionLength;
|
||||
static const std::uint32_t kDataSizeLength;
|
||||
static const std::uint32_t kMinAdvertisementLength;
|
||||
static const std::uint32_t kMaxDataSize;
|
||||
static const std::uint16_t kVersionBitmask;
|
||||
static const std::uint16_t kSocketVersionBitmask;
|
||||
|
||||
BLEAdvertisement(Version::Value version, SocketVersion::Value socket_version,
|
||||
ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data);
|
||||
|
||||
const Version::Value version_;
|
||||
const SocketVersion::Value socket_version_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > service_id_hash_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > data_;
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
|
||||
@@ -1,209 +0,0 @@
|
||||
#include "core/internal/mediums/ble_advertisement_header.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "platform/base64_utils.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// The following IfThisThenThat is for BloomFilter length in
|
||||
// ble_v2.createAdvertisementHeader
|
||||
// LINT.IfChange
|
||||
const std::uint32_t BLEAdvertisementHeader::kServiceIdBloomFilterLength = 10;
|
||||
// LINT.ThenChange(cpp/core/internal/mediums/ble_v2.h)
|
||||
const std::uint32_t BLEAdvertisementHeader::kAdvertisementHashLength = 4;
|
||||
|
||||
const std::uint32_t BLEAdvertisementHeader::kVersionAndNumSlotsLength = 1;
|
||||
const std::uint32_t BLEAdvertisementHeader::kMinAdvertisementHeaderLength =
|
||||
kVersionAndNumSlotsLength + kServiceIdBloomFilterLength +
|
||||
kAdvertisementHashLength;
|
||||
const std::uint16_t BLEAdvertisementHeader::kVersionBitmask = 0x0E0;
|
||||
const std::uint16_t BLEAdvertisementHeader::kNumSlotsBitmask = 0x01F;
|
||||
|
||||
ConstPtr<BLEAdvertisementHeader> BLEAdvertisementHeader::fromString(
|
||||
const std::string &ble_advertisement_header_string) {
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_ble_advertisement_header_bytes(
|
||||
Base64Utils::decode(ble_advertisement_header_string));
|
||||
if (scoped_ble_advertisement_header_bytes.isNull()) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Cannot deserialize BLEAdvertisementHeader: failed Base64 decoding");
|
||||
return ConstPtr<BLEAdvertisementHeader>();
|
||||
}
|
||||
|
||||
if (scoped_ble_advertisement_header_bytes->size() <
|
||||
kMinAdvertisementHeaderLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot deserialize BLEAdvertisementHeader: expecting min %u "
|
||||
"raw bytes, got %zu instead",
|
||||
kMinAdvertisementHeaderLength,
|
||||
scoped_ble_advertisement_header_bytes->size());
|
||||
return ConstPtr<BLEAdvertisementHeader>();
|
||||
}
|
||||
|
||||
// Now, time to read the bytes!
|
||||
const char *ble_advertisement_header_read_ptr =
|
||||
scoped_ble_advertisement_header_bytes->getData();
|
||||
|
||||
// 1. Version.
|
||||
// The first 3 bits of the first byte represent the version.
|
||||
Version::Value version = parseVersionFromByte(
|
||||
static_cast<std::uint16_t>(*ble_advertisement_header_read_ptr));
|
||||
if (version != Version::V2) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Cannot deserialize BLEAdvertisementHeader, unsupported version %u",
|
||||
version);
|
||||
return ConstPtr<BLEAdvertisementHeader>();
|
||||
}
|
||||
|
||||
// 2. Number of slots.
|
||||
// The last 5 bits of the first byte represent the number of slots.
|
||||
std::uint32_t num_slots = parseNumSlotsFromByte(
|
||||
static_cast<std::uint16_t>(*ble_advertisement_header_read_ptr));
|
||||
ble_advertisement_header_read_ptr += kVersionAndNumSlotsLength;
|
||||
|
||||
// 3. Service ID bloom filter.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(
|
||||
MakeConstPtr(new ByteArray(ble_advertisement_header_read_ptr,
|
||||
kServiceIdBloomFilterLength)));
|
||||
ble_advertisement_header_read_ptr += kServiceIdBloomFilterLength;
|
||||
|
||||
// 4. Advertisement hash.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(ble_advertisement_header_read_ptr,
|
||||
kAdvertisementHashLength)));
|
||||
ble_advertisement_header_read_ptr += kAdvertisementHashLength;
|
||||
|
||||
return MakeRefCountedConstPtr(new BLEAdvertisementHeader(
|
||||
version, num_slots, scoped_service_id_bloom_filter.release(),
|
||||
scoped_advertisement_hash.release()));
|
||||
}
|
||||
|
||||
std::string BLEAdvertisementHeader::asString(
|
||||
Version::Value version, std::uint32_t num_slots,
|
||||
ConstPtr<ByteArray> service_id_bloom_filter,
|
||||
ConstPtr<ByteArray> advertisement_hash) {
|
||||
// Check that the given input is valid.
|
||||
if (version != Version::V2) {
|
||||
NEARBY_LOG(
|
||||
INFO, "Cannot serialize BLEAdvertisementHeader: unsupported Version %u",
|
||||
version);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (service_id_bloom_filter->size() != kServiceIdBloomFilterLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEAdvertisementHeader: expected "
|
||||
"service_id_bloom_filter of %u bytes, but got %zu",
|
||||
kServiceIdBloomFilterLength, service_id_bloom_filter->size());
|
||||
return "";
|
||||
}
|
||||
|
||||
if (advertisement_hash->size() != kAdvertisementHashLength) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEAdvertisementHeader: expected "
|
||||
"advertisement_hash of %u bytes, but got %zu",
|
||||
kAdvertisementHashLength, advertisement_hash->size());
|
||||
return "";
|
||||
}
|
||||
|
||||
// Initialize the bytes.
|
||||
ByteArray advertisement_header_bytes{kMinAdvertisementHeaderLength};
|
||||
char *advertisement_header_bytes_write_ptr =
|
||||
advertisement_header_bytes.getData();
|
||||
|
||||
// 1. Version.
|
||||
serializeVersionByte(advertisement_header_bytes_write_ptr, version);
|
||||
|
||||
// 2. Number of slots.
|
||||
serializeNumSlots(advertisement_header_bytes_write_ptr, num_slots);
|
||||
advertisement_header_bytes_write_ptr += kVersionAndNumSlotsLength;
|
||||
|
||||
// 3. Service ID bloom filter.
|
||||
memcpy(advertisement_header_bytes_write_ptr,
|
||||
service_id_bloom_filter->getData(), kServiceIdBloomFilterLength);
|
||||
advertisement_header_bytes_write_ptr += kServiceIdBloomFilterLength;
|
||||
|
||||
// 4. Advertisement hash.
|
||||
memcpy(advertisement_header_bytes_write_ptr, advertisement_hash->getData(),
|
||||
kAdvertisementHashLength);
|
||||
advertisement_header_bytes_write_ptr += kAdvertisementHashLength;
|
||||
|
||||
// Header needs to be binary safe, so apply a Base64 encoding.
|
||||
return Base64Utils::encode(advertisement_header_bytes);
|
||||
}
|
||||
|
||||
BLEAdvertisementHeader::Version::Value
|
||||
BLEAdvertisementHeader::parseVersionFromByte(std::uint16_t byte) {
|
||||
return static_cast<Version::Value>((byte & kVersionBitmask) >> 5);
|
||||
}
|
||||
|
||||
std::uint32_t BLEAdvertisementHeader::parseNumSlotsFromByte(
|
||||
std::uint16_t byte) {
|
||||
return static_cast<std::uint32_t>((byte & kNumSlotsBitmask));
|
||||
}
|
||||
|
||||
void BLEAdvertisementHeader::serializeVersionByte(char *version_byte_write_ptr,
|
||||
Version::Value version) {
|
||||
*version_byte_write_ptr |=
|
||||
static_cast<char>((version << 5) & kVersionBitmask);
|
||||
}
|
||||
|
||||
void BLEAdvertisementHeader::serializeNumSlots(char *num_slots_byte_write_ptr,
|
||||
std::uint32_t num_slots) {
|
||||
*num_slots_byte_write_ptr |= static_cast<char>(num_slots & kNumSlotsBitmask);
|
||||
}
|
||||
|
||||
BLEAdvertisementHeader::BLEAdvertisementHeader(
|
||||
BLEAdvertisementHeader::Version::Value version, std::uint32_t num_slots,
|
||||
ConstPtr<ByteArray> service_id_bloom_filter,
|
||||
ConstPtr<ByteArray> advertisement_hash)
|
||||
: version_(version),
|
||||
num_slots_(num_slots),
|
||||
service_id_bloom_filter_(service_id_bloom_filter),
|
||||
advertisement_hash_(advertisement_hash) {}
|
||||
|
||||
BLEAdvertisementHeader::~BLEAdvertisementHeader() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
BLEAdvertisementHeader::Version::Value BLEAdvertisementHeader::getVersion()
|
||||
const {
|
||||
return version_;
|
||||
}
|
||||
|
||||
std::uint32_t BLEAdvertisementHeader::getNumSlots() const { return num_slots_; }
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisementHeader::getServiceIdBloomFilter() const {
|
||||
return service_id_bloom_filter_.get();
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEAdvertisementHeader::getAdvertisementHash() const {
|
||||
return advertisement_hash_.get();
|
||||
}
|
||||
|
||||
bool BLEAdvertisementHeader::operator<(
|
||||
const BLEAdvertisementHeader &rhs) const {
|
||||
if (this->getVersion() != rhs.getVersion()) {
|
||||
return this->getVersion() < rhs.getVersion();
|
||||
}
|
||||
if (this->getNumSlots() != rhs.getNumSlots()) {
|
||||
return this->getNumSlots() < rhs.getNumSlots();
|
||||
}
|
||||
if (*(this->getServiceIdBloomFilter()) != *(rhs.getServiceIdBloomFilter())) {
|
||||
return *(this->getServiceIdBloomFilter()) <
|
||||
*(rhs.getServiceIdBloomFilter());
|
||||
}
|
||||
return *(this->getAdvertisementHash()) < *(rhs.getAdvertisementHash());
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,91 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_HEADER_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_HEADER_H_
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// Represents the format of the Mediums BLE Advertisement Header used in
|
||||
// Advertising + Discovery.
|
||||
//
|
||||
// [VERSION][NUM_SLOTS][SERVICE_ID_BLOOM_FILTER][ADVERTISEMENT_HASH]
|
||||
//
|
||||
// See go/nearby-ble-design for more information.
|
||||
class BLEAdvertisementHeader {
|
||||
public:
|
||||
// Versions of the BLEAdvertisementHeader.
|
||||
struct Version {
|
||||
enum Value {
|
||||
V2 = 2,
|
||||
// Version is only allocated 3 bits in the BLEAdvertisementHeader, so this
|
||||
// can never go beyond V7.
|
||||
//
|
||||
// V1 is not present because it's an old format used in Nearby Connections
|
||||
// before this logic was pushed down into Nearby Mediums. V1 put
|
||||
// everything in the service data, while V2 puts the data inside a GATT
|
||||
// characteristic so the two are not compatible.
|
||||
};
|
||||
};
|
||||
|
||||
static ConstPtr<BLEAdvertisementHeader> fromString(
|
||||
const std::string &ble_advertisement_header_string);
|
||||
|
||||
static std::string asString(Version::Value version, std::uint32_t num_slots,
|
||||
ConstPtr<ByteArray> service_id_bloom_filter,
|
||||
ConstPtr<ByteArray> advertisement_hash);
|
||||
|
||||
static const std::uint32_t kServiceIdBloomFilterLength;
|
||||
static const std::uint32_t kAdvertisementHashLength;
|
||||
|
||||
~BLEAdvertisementHeader();
|
||||
|
||||
Version::Value getVersion() const;
|
||||
std::uint32_t getNumSlots() const;
|
||||
ConstPtr<ByteArray> getServiceIdBloomFilter() const;
|
||||
ConstPtr<ByteArray> getAdvertisementHash() const;
|
||||
|
||||
// Operator overloads when comparing ConstPtr<BLEAdvertisementHeader>.
|
||||
bool operator<(const BLEAdvertisementHeader &rhs) const;
|
||||
|
||||
private:
|
||||
// DiscoveredPeripheralTracker needs to be a friend of this class because it
|
||||
// directly calls the constructor (the Java code keeps the constructor package
|
||||
// private).
|
||||
// Calling the constuctor directly allows us to avoid the unnessary extra
|
||||
// calls to parse and decode to get the BLEAdvertisementHeader.
|
||||
template <typename>
|
||||
friend class DiscoveredPeripheralTracker;
|
||||
|
||||
static Version::Value parseVersionFromByte(std::uint16_t byte);
|
||||
static std::uint32_t parseNumSlotsFromByte(std::uint16_t byte);
|
||||
|
||||
static const std::uint32_t kVersionAndNumSlotsLength;
|
||||
static const std::uint32_t kMinAdvertisementHeaderLength;
|
||||
static const std::uint16_t kVersionBitmask;
|
||||
static const std::uint16_t kNumSlotsBitmask;
|
||||
|
||||
BLEAdvertisementHeader(Version::Value version, std::uint32_t num_slots,
|
||||
ConstPtr<ByteArray> service_id_bloom_filter,
|
||||
ConstPtr<ByteArray> advertisement_hash);
|
||||
|
||||
static void serializeVersionByte(char *version_byte_write_ptr,
|
||||
Version::Value version);
|
||||
static void serializeNumSlots(char *num_slots_byte_write_ptr,
|
||||
std::uint32_t num_slots);
|
||||
|
||||
const Version::Value version_;
|
||||
const uint32_t num_slots_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > service_id_bloom_filter_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > advertisement_hash_;
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_HEADER_H_
|
||||
@@ -1,221 +0,0 @@
|
||||
#include "core/internal/mediums/ble_advertisement_header.h"
|
||||
|
||||
#include "platform/base64_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
const BLEAdvertisementHeader::Version::Value kVersion =
|
||||
BLEAdvertisementHeader::Version::V2;
|
||||
const std::uint32_t kNumSlots = 2;
|
||||
const char kServiceIDBloomFilter[] = {0x01, 0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x08, 0x09, 0x0A};
|
||||
const char kAdvertisementHash[] = {0x0A, 0x0B, 0x0C, 0x0D};
|
||||
const size_t kAdvertisementHeaderLength = 15;
|
||||
const size_t kLongAdvertisementHeaderLength = kAdvertisementHeaderLength + 1;
|
||||
const size_t kShortAdvertisementHeaderLength = kAdvertisementHeaderLength - 1;
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationDeserializationWorks) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
kVersion, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisementHeader> > scoped_ble_advertisement_header(
|
||||
BLEAdvertisementHeader::fromString(ble_advertisement_header_string));
|
||||
|
||||
ASSERT_EQ(kVersion, scoped_ble_advertisement_header->getVersion());
|
||||
ASSERT_EQ(kNumSlots, scoped_ble_advertisement_header->getNumSlots());
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
memcmp(
|
||||
kServiceIDBloomFilter,
|
||||
scoped_ble_advertisement_header->getServiceIdBloomFilter()->getData(),
|
||||
scoped_ble_advertisement_header->getServiceIdBloomFilter()->size()));
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
memcmp(kAdvertisementHash,
|
||||
scoped_ble_advertisement_header->getAdvertisementHash()->getData(),
|
||||
scoped_ble_advertisement_header->getAdvertisementHash()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationFailsWithBadVersion) {
|
||||
BLEAdvertisementHeader::Version::Value bad_version =
|
||||
static_cast<BLEAdvertisementHeader::Version::Value>(666);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
bad_version, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
|
||||
ASSERT_EQ("", ble_advertisement_header_string);
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationFailsWithShortServiceIdBloomFilter) {
|
||||
char short_service_id_bloom_filter[] = {0x01, 0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x08, 0x09};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(short_service_id_bloom_filter,
|
||||
sizeof(short_service_id_bloom_filter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
kVersion, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
|
||||
ASSERT_EQ("", ble_advertisement_header_string);
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationFailsWithLongServiceIdBloomFilter) {
|
||||
char long_service_id_bloom_filter[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0A, 0x0B};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(long_service_id_bloom_filter,
|
||||
sizeof(long_service_id_bloom_filter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
kVersion, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
|
||||
ASSERT_EQ("", ble_advertisement_header_string);
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationFailsWithShortAdvertisementHash) {
|
||||
char short_advertisement_hash[] = {0x0A, 0x0B, 0x0C};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(MakeConstPtr(
|
||||
new ByteArray(short_advertisement_hash,
|
||||
sizeof(short_advertisement_hash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
kVersion, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
|
||||
ASSERT_EQ("", ble_advertisement_header_string);
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, SerializationFailsWithLongAdvertisementHash) {
|
||||
char long_advertisement_hash[] = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(MakeConstPtr(
|
||||
new ByteArray(long_advertisement_hash,
|
||||
sizeof(long_advertisement_hash) / sizeof(char))));
|
||||
|
||||
std::string ble_advertisement_header_string(BLEAdvertisementHeader::asString(
|
||||
kVersion, kNumSlots, scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get()));
|
||||
|
||||
ASSERT_EQ("", ble_advertisement_header_string);
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, DeserializationWorksWithExtraBytes) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
std::string ble_advertisement_header_string =
|
||||
BLEAdvertisementHeader::asString(kVersion, kNumSlots,
|
||||
scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get());
|
||||
|
||||
// Base64 decode the string, add a character, and then re-encode it. We must
|
||||
// explicitly define how long our array is because we can't use variable
|
||||
// length arrays.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_ble_advertisement_header_bytes(
|
||||
Base64Utils::decode(ble_advertisement_header_string));
|
||||
char raw_long_ble_advertisement_header_bytes[kLongAdvertisementHeaderLength];
|
||||
memcpy(raw_long_ble_advertisement_header_bytes,
|
||||
scoped_ble_advertisement_header_bytes->getData(),
|
||||
kLongAdvertisementHeaderLength);
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_long_ble_advertisement_header_bytes(
|
||||
MakeConstPtr(new ByteArray(raw_long_ble_advertisement_header_bytes,
|
||||
kLongAdvertisementHeaderLength)));
|
||||
std::string long_ble_advertisement_header_string =
|
||||
Base64Utils::encode(scoped_long_ble_advertisement_header_bytes.get());
|
||||
|
||||
ScopedPtr<ConstPtr<BLEAdvertisementHeader> > scoped_ble_advertisement_header(
|
||||
BLEAdvertisementHeader::fromString(long_ble_advertisement_header_string));
|
||||
|
||||
ASSERT_EQ(kVersion, scoped_ble_advertisement_header->getVersion());
|
||||
ASSERT_EQ(kNumSlots, scoped_ble_advertisement_header->getNumSlots());
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
memcmp(
|
||||
kServiceIDBloomFilter,
|
||||
scoped_ble_advertisement_header->getServiceIdBloomFilter()->getData(),
|
||||
scoped_ble_advertisement_header->getServiceIdBloomFilter()->size()));
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
memcmp(kAdvertisementHash,
|
||||
scoped_ble_advertisement_header->getAdvertisementHash()->getData(),
|
||||
scoped_ble_advertisement_header->getAdvertisementHash()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementHeader, DeserializationFailsWithShortLength) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_bloom_filter(MakeConstPtr(
|
||||
new ByteArray(kServiceIDBloomFilter,
|
||||
sizeof(kServiceIDBloomFilter) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_advertisement_hash(
|
||||
MakeConstPtr(new ByteArray(kAdvertisementHash,
|
||||
sizeof(kAdvertisementHash) / sizeof(char))));
|
||||
std::string ble_advertisement_header_string =
|
||||
BLEAdvertisementHeader::asString(kVersion, kNumSlots,
|
||||
scoped_service_id_bloom_filter.get(),
|
||||
scoped_advertisement_hash.get());
|
||||
|
||||
// Base64 decode the string, remove a character, and then re-encode it. We
|
||||
// must explicitly define how long our array is because we can't use variable
|
||||
// length arrays.
|
||||
ScopedPtr<Ptr<ByteArray> > scoped_ble_advertisement_header_bytes(
|
||||
Base64Utils::decode(ble_advertisement_header_string));
|
||||
char
|
||||
raw_short_ble_advertisement_header_bytes[kShortAdvertisementHeaderLength];
|
||||
memcpy(raw_short_ble_advertisement_header_bytes,
|
||||
scoped_ble_advertisement_header_bytes->getData(),
|
||||
kShortAdvertisementHeaderLength);
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_short_ble_advertisement_header_bytes(
|
||||
MakeConstPtr(new ByteArray(raw_short_ble_advertisement_header_bytes,
|
||||
kShortAdvertisementHeaderLength)));
|
||||
std::string short_ble_advertisement_header_string =
|
||||
Base64Utils::encode(scoped_short_ble_advertisement_header_bytes.get());
|
||||
|
||||
ScopedPtr<ConstPtr<BLEAdvertisementHeader> > scoped_ble_advertisement_header(
|
||||
BLEAdvertisementHeader::fromString(
|
||||
short_ble_advertisement_header_string));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_header.isNull());
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,322 +0,0 @@
|
||||
#include "core/internal/mediums/ble_advertisement.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
namespace {
|
||||
|
||||
const BLEAdvertisement::Version::Value kVersion = BLEAdvertisement::Version::V2;
|
||||
const BLEAdvertisement::SocketVersion::Value kSocketVersion =
|
||||
BLEAdvertisement::SocketVersion::V2;
|
||||
const char kServiceIDHashBytes[] = {0x0A, 0x0B, 0x0C};
|
||||
const char kData[] =
|
||||
"How much wood can a woodchuck chuck if a wood chuck would chuck wood?";
|
||||
// This corresponds to the length of a specific BLEAdvertisement packed with the
|
||||
// kData given above. Be sure to update this if kData ever changes.
|
||||
const size_t kAdvertisementLength = 77;
|
||||
const size_t kLongAdvertisementLength = kAdvertisementLength + 1000;
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationWorksV1) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
BLEAdvertisement::Version::V1, BLEAdvertisement::SocketVersion::V1,
|
||||
scoped_service_id_hash.get(), scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(BLEAdvertisement::Version::V1,
|
||||
scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(BLEAdvertisement::SocketVersion::V1,
|
||||
scoped_ble_advertisement->getSocketVersion());
|
||||
ASSERT_EQ(scoped_service_id_hash->size(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(kServiceIDHashBytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(scoped_data->size(), scoped_ble_advertisement->getData()->size());
|
||||
ASSERT_EQ(0, memcmp(kData, scoped_ble_advertisement->getData()->getData(),
|
||||
scoped_ble_advertisement->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationWorks) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(kVersion, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(kSocketVersion, scoped_ble_advertisement->getSocketVersion());
|
||||
ASSERT_EQ(scoped_service_id_hash->size(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(kServiceIDHashBytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(scoped_data->size(), scoped_ble_advertisement->getData()->size());
|
||||
ASSERT_EQ(0, memcmp(kData, scoped_ble_advertisement->getData()->getData(),
|
||||
scoped_ble_advertisement->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationWorksWithEmptyData) {
|
||||
char empty_data[0];
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(MakeConstPtr(
|
||||
new ByteArray(empty_data, sizeof(empty_data) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(kVersion, scoped_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(kSocketVersion, scoped_ble_advertisement->getSocketVersion());
|
||||
ASSERT_EQ(scoped_service_id_hash->size(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0, memcmp(kServiceIDHashBytes,
|
||||
scoped_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(scoped_data->size(), scoped_ble_advertisement->getData()->size());
|
||||
ASSERT_EQ(0, memcmp(kData, scoped_ble_advertisement->getData()->getData(),
|
||||
scoped_ble_advertisement->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationDeserializationFailsWithLargeData) {
|
||||
// Create data that's larger than the allowed size.
|
||||
char large_data[513];
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(MakeConstPtr(
|
||||
new ByteArray(large_data, sizeof(large_data) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithBadVersion) {
|
||||
BLEAdvertisement::Version::Value bad_version =
|
||||
static_cast<BLEAdvertisement::Version::Value>(666);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(bad_version, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithBadSocketVersion) {
|
||||
BLEAdvertisement::SocketVersion::Value bad_socket_version =
|
||||
static_cast<BLEAdvertisement::SocketVersion::Value>(666);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, bad_socket_version,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithShortServiceIdHash) {
|
||||
char short_service_id_hash_bytes[] = {0x0A, 0x0B};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(short_service_id_hash_bytes,
|
||||
sizeof(short_service_id_hash_bytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithLongServiceIdHash) {
|
||||
char long_service_id_hash_bytes[] = {0x0A, 0x0B, 0x0C, 0x0D};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(long_service_id_hash_bytes,
|
||||
sizeof(long_service_id_hash_bytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, SerializationFailsWithLongData) {
|
||||
// BLEAdvertisement shouldn't be able to support data with the max GATT
|
||||
// attribute length because it needs some room for the preceding fields.
|
||||
char long_data[512];
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(long_data, sizeof(long_data) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationWorksWithExtraBytes) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
// Copy the bytes into a new array with extra bytes. We must explicitly
|
||||
// define how long our array is because we can't use variable length arrays.
|
||||
char raw_ble_advertisement_bytes[kLongAdvertisementLength] {};
|
||||
memcpy(raw_ble_advertisement_bytes, scoped_ble_advertisement_bytes->getData(),
|
||||
std::min(sizeof(raw_ble_advertisement_bytes),
|
||||
scoped_ble_advertisement_bytes->size()));
|
||||
|
||||
// Re-parse the BLE advertisement using our extra long advertisement bytes.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_long_ble_advertisement_bytes(
|
||||
MakeConstPtr(new ByteArray(raw_ble_advertisement_bytes,
|
||||
kLongAdvertisementLength)));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_long_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_long_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_EQ(kVersion, scoped_long_ble_advertisement->getVersion());
|
||||
ASSERT_EQ(kSocketVersion, scoped_long_ble_advertisement->getSocketVersion());
|
||||
ASSERT_EQ(scoped_service_id_hash->size(),
|
||||
scoped_long_ble_advertisement->getServiceIdHash()->size());
|
||||
ASSERT_EQ(0,
|
||||
memcmp(kServiceIDHashBytes,
|
||||
scoped_long_ble_advertisement->getServiceIdHash()->getData(),
|
||||
scoped_long_ble_advertisement->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(scoped_data->size(),
|
||||
scoped_long_ble_advertisement->getData()->size());
|
||||
ASSERT_EQ(0,
|
||||
memcmp(kData, scoped_long_ble_advertisement->getData()->getData(),
|
||||
scoped_long_ble_advertisement->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithNullBytes) {
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(ConstPtr<ByteArray>()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithShortLength) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
// Cut off the advertisement so that it's too short.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_short_ble_advertisement_bytes(
|
||||
MakeConstPtr(
|
||||
new ByteArray(scoped_ble_advertisement_bytes->getData(), 7)));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(scoped_short_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEAdvertisementTest, DeserializationFailsWithInvalidDataLength) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(kServiceIDHashBytes,
|
||||
sizeof(kServiceIDHashBytes) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(kVersion, kSocketVersion,
|
||||
scoped_service_id_hash.get(),
|
||||
scoped_data.get()));
|
||||
|
||||
// Corrupt the DATA_SIZE bits. Start by making a raw copy of the BLE
|
||||
// advertisement bytes so we can modify it. We must explicitly define how long
|
||||
// our array is because we can't use variable length arrays.
|
||||
char raw_ble_advertisement_bytes[kAdvertisementLength];
|
||||
memcpy(raw_ble_advertisement_bytes, scoped_ble_advertisement_bytes->getData(),
|
||||
kAdvertisementLength);
|
||||
|
||||
// The data size field lives in indices 4-7. Corrupt it.
|
||||
memset(raw_ble_advertisement_bytes + 4, 0xFF, 4);
|
||||
|
||||
// Try to parse the BLE advertisement using our corrupted advertisement bytes.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_corrupted_ble_advertisement_bytes(
|
||||
MakeConstPtr(
|
||||
new ByteArray(raw_ble_advertisement_bytes, kAdvertisementLength)));
|
||||
ScopedPtr<ConstPtr<BLEAdvertisement> > scoped_ble_advertisement(
|
||||
BLEAdvertisement::fromBytes(
|
||||
scoped_corrupted_ble_advertisement_bytes.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_advertisement.isNull());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,113 +0,0 @@
|
||||
#include "core/internal/mediums/ble_packet.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "platform/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
const std::uint32_t BLEPacket::kServiceIdHashLength = 3;
|
||||
|
||||
const std::uint32_t BLEPacket::kMinPacketLength = kServiceIdHashLength;
|
||||
const std::uint32_t BLEPacket::kMaxDataSize =
|
||||
std::numeric_limits<int32_t>::max() - kMinPacketLength;
|
||||
|
||||
ConstPtr<BLEPacket> BLEPacket::fromBytes(ConstPtr<ByteArray> ble_packet_bytes) {
|
||||
if (ble_packet_bytes.isNull()) {
|
||||
NEARBY_LOG(INFO, "Cannot deserialize BLEPacket: null bytes passed in");
|
||||
return ConstPtr<BLEPacket>();
|
||||
}
|
||||
|
||||
if (ble_packet_bytes->size() < kMinPacketLength) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Cannot deserialize BLEPacket: expecting min %u raw bytes, got %zu",
|
||||
kMinPacketLength, ble_packet_bytes->size());
|
||||
return ConstPtr<BLEPacket>();
|
||||
}
|
||||
|
||||
// Now, time to read the bytes!
|
||||
const char *ble_packet_bytes_read_ptr = ble_packet_bytes->getData();
|
||||
|
||||
// 1. Service ID hash.
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(ble_packet_bytes_read_ptr, kServiceIdHashLength)));
|
||||
ble_packet_bytes_read_ptr += kServiceIdHashLength;
|
||||
|
||||
// 2. Data.
|
||||
size_t data_size = computeDataSize(ble_packet_bytes);
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(ble_packet_bytes_read_ptr, data_size)));
|
||||
ble_packet_bytes_read_ptr += data_size;
|
||||
|
||||
return MakeConstPtr(
|
||||
new BLEPacket(scoped_service_id_hash.release(), scoped_data.release()));
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEPacket::toBytes(ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data) {
|
||||
if (service_id_hash->size() != kServiceIdHashLength) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Cannot serialize BLEPacket: expected a service_id_hash of %u bytes, "
|
||||
"but got %zu",
|
||||
kServiceIdHashLength, service_id_hash->size());
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
if (data->size() > kMaxDataSize) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Cannot serialize BLEPacket: expected data of at most %u bytes, "
|
||||
"but got %zu",
|
||||
kMaxDataSize, data->size());
|
||||
return ConstPtr<ByteArray>();
|
||||
}
|
||||
|
||||
// Initialize the bytes.
|
||||
size_t packet_length = computePacketLength(data);
|
||||
Ptr<ByteArray> packet_bytes{new ByteArray{packet_length}};
|
||||
char *packet_bytes_write_ptr = packet_bytes->getData();
|
||||
|
||||
// 1. Service ID hash.
|
||||
memcpy(packet_bytes_write_ptr, service_id_hash->getData(),
|
||||
kServiceIdHashLength);
|
||||
packet_bytes_write_ptr += kServiceIdHashLength;
|
||||
|
||||
// 2. Data.
|
||||
memcpy(packet_bytes_write_ptr, data->getData(), data->size());
|
||||
packet_bytes_write_ptr += data->size();
|
||||
|
||||
return ConstifyPtr(packet_bytes);
|
||||
}
|
||||
|
||||
size_t BLEPacket::computeDataSize(ConstPtr<ByteArray> ble_packet_bytes) {
|
||||
return ble_packet_bytes->size() - kMinPacketLength;
|
||||
}
|
||||
|
||||
size_t BLEPacket::computePacketLength(ConstPtr<ByteArray> data) {
|
||||
// The packet length is the minimum length + the length of the data.
|
||||
return kMinPacketLength + data->size();
|
||||
}
|
||||
|
||||
BLEPacket::BLEPacket(ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data)
|
||||
: service_id_hash_(service_id_hash), data_(data) {}
|
||||
|
||||
BLEPacket::~BLEPacket() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEPacket::getServiceIdHash() const {
|
||||
return service_id_hash_.get();
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEPacket::getData() const { return data_.get(); }
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_PACKET_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_PACKET_H_
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// Represents the format of data sent over BLE sockets.
|
||||
//
|
||||
// [SERVICE_ID_HASH][DATA]
|
||||
//
|
||||
// See go/nearby-ble-design for more information.
|
||||
class BLEPacket {
|
||||
public:
|
||||
static ConstPtr<BLEPacket> fromBytes(ConstPtr<ByteArray> ble_packet_bytes);
|
||||
|
||||
static ConstPtr<ByteArray> toBytes(ConstPtr<ByteArray> service_id_hash,
|
||||
ConstPtr<ByteArray> data);
|
||||
|
||||
static const std::uint32_t kServiceIdHashLength;
|
||||
|
||||
~BLEPacket();
|
||||
|
||||
ConstPtr<ByteArray> getServiceIdHash() const;
|
||||
ConstPtr<ByteArray> getData() const;
|
||||
|
||||
private:
|
||||
static size_t computeDataSize(ConstPtr<ByteArray> ble_packet_bytes);
|
||||
static size_t computePacketLength(ConstPtr<ByteArray> data);
|
||||
|
||||
static const std::uint32_t kMinPacketLength;
|
||||
static const std::uint32_t kMaxDataSize;
|
||||
|
||||
BLEPacket(ConstPtr<ByteArray> service_id_hash, ConstPtr<ByteArray> data);
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > service_id_hash_;
|
||||
ScopedPtr<ConstPtr<ByteArray> > data_;
|
||||
};
|
||||
|
||||
// Represents the format of data sent over BLE sockets.
|
||||
//
|
||||
// [SERVICE_ID_HASH][DATA]
|
||||
//
|
||||
// See go/nearby-ble-design for more information.
|
||||
class BlePacket {
|
||||
public:
|
||||
static BlePacket FromBytes(const ByteArray& bytes);
|
||||
|
||||
static ByteArray ToBytes(const ByteArray& service_id_hash,
|
||||
const ByteArray& data);
|
||||
|
||||
static const uint32_t kServiceIdHashLength;
|
||||
|
||||
~BlePacket();
|
||||
|
||||
ByteArray GetServiceIdHash() const;
|
||||
ByteArray GetData() const;
|
||||
|
||||
private:
|
||||
static size_t ComputeDataSize(const ByteArray& ble_packet_bytes);
|
||||
static size_t ComputePacketLength(const ByteArray& data);
|
||||
|
||||
static const uint32_t kMinPacketLength;
|
||||
static const uint32_t kMaxDataSize;
|
||||
|
||||
BlePacket(const ByteArray& service_id_hash, const ByteArray& data);
|
||||
|
||||
ByteArray service_id_hash_;
|
||||
ByteArray data_;
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_PACKET_H_
|
||||
@@ -1,108 +0,0 @@
|
||||
#include "core/internal/mediums/ble_packet.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
const char kServiceIDHash[] = {0x0A, 0x0B, 0x0C};
|
||||
const char kData[] = {0x00, 0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
TEST(BLEPacket, SerializationDeserializationWorks) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(kServiceIDHash, sizeof(kServiceIDHash) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_packet_bytes(
|
||||
BLEPacket::toBytes(scoped_service_id_hash.get(), scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEPacket> > scoped_ble_packet(
|
||||
BLEPacket::fromBytes(scoped_ble_packet_bytes.get()));
|
||||
|
||||
ASSERT_EQ(0, memcmp(kServiceIDHash,
|
||||
scoped_ble_packet->getServiceIdHash()->getData(),
|
||||
scoped_ble_packet->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(0, memcmp(kData, scoped_ble_packet->getData()->getData(),
|
||||
scoped_ble_packet->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEPacket, SerializationDeserializationWorksWithEmptyData) {
|
||||
char empty_data[] = {};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(kServiceIDHash, sizeof(kServiceIDHash) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(MakeConstPtr(
|
||||
new ByteArray(empty_data, sizeof(empty_data) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_packet_bytes(
|
||||
BLEPacket::toBytes(scoped_service_id_hash.get(), scoped_data.get()));
|
||||
ScopedPtr<ConstPtr<BLEPacket> > scoped_ble_packet(
|
||||
BLEPacket::fromBytes(scoped_ble_packet_bytes.get()));
|
||||
|
||||
ASSERT_EQ(0, memcmp(kServiceIDHash,
|
||||
scoped_ble_packet->getServiceIdHash()->getData(),
|
||||
scoped_ble_packet->getServiceIdHash()->size()));
|
||||
ASSERT_EQ(0, memcmp(empty_data, scoped_ble_packet->getData()->getData(),
|
||||
scoped_ble_packet->getData()->size()));
|
||||
}
|
||||
|
||||
TEST(BLEPacket, SerializationFailsWithShortServiceIdHash) {
|
||||
char short_service_id_hash[] = {0x0A, 0x0B};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(short_service_id_hash,
|
||||
sizeof(short_service_id_hash) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_packet_bytes(
|
||||
BLEPacket::toBytes(scoped_service_id_hash.get(), scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_packet_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEPacket, SerializationFailsWithLongServiceIdHash) {
|
||||
char long_service_id_hash[]{0x0A, 0x0B, 0x0C, 0x0D};
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(
|
||||
MakeConstPtr(new ByteArray(long_service_id_hash,
|
||||
sizeof(long_service_id_hash) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_packet_bytes(
|
||||
BLEPacket::toBytes(scoped_service_id_hash.get(), scoped_data.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_packet_bytes.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEPacket, DeserializationFailsWithNullBytes) {
|
||||
ScopedPtr<ConstPtr<BLEPacket> > scoped_ble_packet(
|
||||
BLEPacket::fromBytes(ConstPtr<ByteArray>()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_packet.isNull());
|
||||
}
|
||||
|
||||
TEST(BLEPacket, DeserializationFailsWithShortLength) {
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
|
||||
new ByteArray(kServiceIDHash, sizeof(kServiceIDHash) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_data(
|
||||
MakeConstPtr(new ByteArray(kData, sizeof(kData) / sizeof(char))));
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_ble_packet_bytes(
|
||||
BLEPacket::toBytes(scoped_service_id_hash.get(), scoped_data.get()));
|
||||
|
||||
// Cut off the packet so that it's too short
|
||||
ScopedPtr<ConstPtr<ByteArray> > scoped_short_ble_packet_bytes(
|
||||
MakeConstPtr(new ByteArray(scoped_ble_packet_bytes->getData(), 2)));
|
||||
ScopedPtr<ConstPtr<BLEPacket> > scoped_ble_packet(
|
||||
BLEPacket::fromBytes(scoped_short_ble_packet_bytes.get()));
|
||||
|
||||
ASSERT_TRUE(scoped_ble_packet.isNull());
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "core/internal/mediums/ble_peripheral.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
BLEPeripheral::BLEPeripheral(ConstPtr<ByteArray> id) : id_(id) {}
|
||||
|
||||
BLEPeripheral::~BLEPeripheral() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
ConstPtr<ByteArray> BLEPeripheral::getId() const { return id_.get(); }
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,45 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_PERIPHERAL_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_PERIPHERAL_H_
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
class BLEPeripheral {
|
||||
public:
|
||||
explicit BLEPeripheral(ConstPtr<ByteArray> id);
|
||||
~BLEPeripheral();
|
||||
|
||||
ConstPtr<ByteArray> getId() const;
|
||||
|
||||
private:
|
||||
// A unique identifier for this peripheral. It can be the BLE advertisement it
|
||||
// was found on, or even simply the BLE MAC address.
|
||||
ScopedPtr<ConstPtr<ByteArray>> id_;
|
||||
};
|
||||
|
||||
|
||||
// Represents BLE peripheral for testing.
|
||||
class BlePeripheral {
|
||||
public:
|
||||
explicit BlePeripheral(const ByteArray& id) : id_(id) {}
|
||||
~BlePeripheral() = default;
|
||||
|
||||
const ByteArray& GetId() const { return id_; }
|
||||
|
||||
private:
|
||||
// A unique identifier for this peripheral. It can be the BLE advertisement it
|
||||
// was found on, or even simply the BLE MAC address.
|
||||
const ByteArray id_;
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_PERIPHERAL_H_
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "core_v2/internal/mediums/ble.h"
|
||||
#include "core/internal/mediums/ble.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_v2/internal/mediums/bluetooth_radio.h"
|
||||
#include "platform_v2/base/medium_environment.h"
|
||||
#include "platform_v2/public/ble.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "core/internal/mediums/bluetooth_radio.h"
|
||||
#include "platform/base/medium_environment.h"
|
||||
#include "platform/public/ble.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@@ -1,826 +0,0 @@
|
||||
#include "core/internal/mediums/ble.h"
|
||||
#include "core/internal/mediums/ble_advertisement_header.h"
|
||||
#include "core/internal/mediums/bloom_filter.h"
|
||||
#include "core/internal/mediums/utils.h"
|
||||
#include "core/internal/mediums/uuid.h"
|
||||
#include "platform/synchronized.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
namespace ble_v2 {
|
||||
|
||||
template <typename Platform>
|
||||
class ProcessOnLostRunnable : public Runnable {
|
||||
public:
|
||||
explicit ProcessOnLostRunnable(Ptr<BLEV2<Platform>> ble_v2)
|
||||
: ble_v2_(ble_v2) {}
|
||||
|
||||
void run() override { ble_v2_->processOnLostTimeout(); }
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> ble_v2_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
class OnAdvertisementFoundRunnable : public Runnable {
|
||||
public:
|
||||
OnAdvertisementFoundRunnable(
|
||||
Ptr<BLEV2<Platform>> ble_v2, Ptr<BLEPeripheralV2> peripheral,
|
||||
ConstPtr<BLEAdvertisementData> advertisement_data)
|
||||
: ble_v2_(ble_v2),
|
||||
peripheral_(peripheral),
|
||||
advertisement_data_(advertisement_data) {}
|
||||
|
||||
// This method is synchronized because it affects class state, but is called
|
||||
// from a separate thread that fires whenever a BLE advertisement is seen.
|
||||
void run() override {
|
||||
Synchronized s(ble_v2_->lock_.get());
|
||||
|
||||
ble_v2_->discovered_peripheral_tracker_->processFoundBleAdvertisement(
|
||||
peripheral_, advertisement_data_.release(),
|
||||
MakePtr(new typename BLEV2<Platform>::GATTAdvertisementFetcherFacade(
|
||||
ble_v2_)));
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> ble_v2_;
|
||||
Ptr<BLEPeripheralV2> peripheral_;
|
||||
ScopedPtr<ConstPtr<BLEAdvertisementData>> advertisement_data_;
|
||||
};
|
||||
|
||||
} // namespace ble_v2
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t BLEV2<Platform>::kNumAdvertisementSlots = 2;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t BLEV2<Platform>::kMaxAdvertisementLength = 512;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t BLEV2<Platform>::kDummyServiceIdLength = 512;
|
||||
|
||||
template <typename Platform>
|
||||
const char* BLEV2<Platform>::kCopresenceServiceUuid =
|
||||
"0000FEF3-0000-1000-8000-00805F9B34FB";
|
||||
|
||||
template <typename Platform>
|
||||
const std::int64_t BLEV2<Platform>::kOnLostTimeoutMillis = 15000;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int64_t BLEV2<Platform>::kGattAdvertisementOperationTimeoutMillis =
|
||||
5000;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int64_t
|
||||
BLEV2<Platform>::kMinConnectionAttemptRecoveryDurationMillis = 1000;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t
|
||||
BLEV2<Platform>::kMaxConnectionAttemptRecoveryFuzzDurationMillis = 10000;
|
||||
|
||||
template <typename Platform>
|
||||
const std::uint32_t BLEV2<Platform>::kDefaultMtu = 512;
|
||||
|
||||
// These two values make up the base UUID we use when advertising a slot. The
|
||||
// base is an all zero Version-3 name-based UUID. To turn this into an
|
||||
// advertisement slot UUID, we simply OR the least significant bits with the
|
||||
// slot number.
|
||||
//
|
||||
// More info about the format can be found here:
|
||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)
|
||||
template <typename Platform>
|
||||
const std::int64_t BLEV2<Platform>::kAdvertisementUuidMsb = 0x0000000000003000;
|
||||
|
||||
template <typename Platform>
|
||||
const std::int64_t BLEV2<Platform>::kAdvertisementUuidLsb = 0x8000000000000000;
|
||||
|
||||
template <typename Platform>
|
||||
BLEV2<Platform>::BLEV2(Ptr<BluetoothRadio<Platform>> bluetooth_radio)
|
||||
: lock_(Platform::createLock()),
|
||||
platform_thread_offloader_(Platform::createSingleThreadExecutor()),
|
||||
prng_(MakePtr(new Prng())),
|
||||
hash_utils_(Platform::createHashUtils()),
|
||||
bluetooth_radio_(bluetooth_radio),
|
||||
bluetooth_adapter_(Platform::createBluetoothAdapter()),
|
||||
ble_medium_(Platform::createBLEMediumV2()),
|
||||
scanning_info_(),
|
||||
discovered_peripheral_tracker_(
|
||||
new DiscoveredPeripheralTracker<Platform>()),
|
||||
on_lost_executor_(Platform::createScheduledExecutor()),
|
||||
advertising_info_(),
|
||||
gatt_server_info_(),
|
||||
accepting_connections_info_() {}
|
||||
|
||||
template <typename Platform>
|
||||
BLEV2<Platform>::~BLEV2() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
on_lost_executor_->shutdown();
|
||||
platform_thread_offloader_->shutdown();
|
||||
stopAdvertising();
|
||||
stopAdvertisementGattServer();
|
||||
stopAcceptingConnections();
|
||||
stopScanning();
|
||||
// discovered_peripheral_tracker is a ScopedPtr member and will take care of
|
||||
// itself.
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::isAvailable() {
|
||||
// This is purposefully left un-synchronized like its java counterpart.
|
||||
// Callers should be able to query this without waiting for other operations
|
||||
// to complete first and this should be safe to call after shutdown. We would
|
||||
// have made it static, but it relies on variables from the constructor (like
|
||||
// ble_medium_ and bluetooth_adapter_).
|
||||
return !ble_medium_.isNull() && !bluetooth_adapter_.isNull();
|
||||
}
|
||||
|
||||
// Returns true if currently scanning for BLE advertisements.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::isAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return !advertising_info_.isNull();
|
||||
}
|
||||
|
||||
// Starts BLE advertising, delivering additional information through a GATT
|
||||
// server.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::startAdvertising(
|
||||
const string& service_id, ConstPtr<ByteArray> advertisement_bytes,
|
||||
BLEMediumV2::PowerMode::Value power_mode,
|
||||
const string& fast_advertisement_service_uuid) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_advertisement_bytes(
|
||||
advertisement_bytes);
|
||||
|
||||
if (service_id.empty() || scoped_advertisement_bytes.isNull()) {
|
||||
// logger.atSevere().log("Refusing to start BLE advertising because a null
|
||||
// parameter was passed in.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scoped_advertisement_bytes->size() > kMaxAdvertisementLength) {
|
||||
// logger.atSevere().log("Refusing to start BLE advertising because the
|
||||
// advertisement was too long. Expected at most %d bytes but received %d.",
|
||||
// kMaxAdvertisementLength, scoped_advertisement_bytes->size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: We don't include logic checking/using the fast_pair_model_id because
|
||||
// that is a java-only concept for now.
|
||||
|
||||
if (isAdvertising()) {
|
||||
// logger.atSevere().log("Failed to BLE advertise because we're already
|
||||
// advertising.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// logger.atSevere().log("Can't start BLE advertising because Bluetooth
|
||||
// isn't enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// logger.atSevere().log("Can't start BLE advertising because BLE is not
|
||||
// available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(ahlee): Remove this check here and in the java code (redundant)
|
||||
// Stop any existing advertisement GATT servers. We don't stop it in
|
||||
// stopAdvertising() to avoid GATT issues with BLE sockets.
|
||||
if (isAdvertisementGattServerRunning()) {
|
||||
stopAdvertisementGattServer();
|
||||
}
|
||||
|
||||
// Start a GATT server to deliver the full advertisement data. If we fail to
|
||||
// advertise the header, we must shut this down before the method returns.
|
||||
bool is_fast_advertisement = !fast_advertisement_service_uuid.empty();
|
||||
if (!is_fast_advertisement) {
|
||||
if (!startAdvertisementGattServer(service_id,
|
||||
scoped_advertisement_bytes.get())) {
|
||||
// logger.atSevere().log("Failed to to BLE advertise because the
|
||||
// advertisement GATT server failed to start");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> advertisement_header_bytes(
|
||||
createAdvertisementHeader(service_id, scoped_advertisement_bytes.get(),
|
||||
is_fast_advertisement));
|
||||
if (advertisement_header_bytes.isNull()) {
|
||||
// logger.atSevere().log("Failed to to BLE advertise because we could not
|
||||
// create an advertisement header");
|
||||
// We failed to start BLE advertising, so stop the advertisement GATT
|
||||
// server.
|
||||
stopAdvertisementGattServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedPtr<Ptr<BLEAdvertisementData>> advertisement(
|
||||
new BLEAdvertisementData());
|
||||
advertisement->is_connectable = true;
|
||||
advertisement->tx_power_level =
|
||||
BLEAdvertisementData::UNSPECIFIED_TX_POWER_LEVEL;
|
||||
|
||||
ScopedPtr<Ptr<BLEAdvertisementData>> scan_response(
|
||||
new BLEAdvertisementData());
|
||||
scan_response->is_connectable = true;
|
||||
scan_response->tx_power_level =
|
||||
BLEAdvertisementData::UNSPECIFIED_TX_POWER_LEVEL;
|
||||
scan_response->service_uuids.insert(kCopresenceServiceUuid);
|
||||
scan_response->service_data.insert(std::make_pair(
|
||||
kCopresenceServiceUuid, advertisement_header_bytes.release()));
|
||||
|
||||
// Note: We don't use fast pair data because that is java-only for now.
|
||||
|
||||
// TODO(ahlee): Fix this if check in the java code.
|
||||
if (is_fast_advertisement) {
|
||||
ScopedPtr<ConstPtr<ByteArray>> service_id_hash(
|
||||
generateServiceIdHash(BLEAdvertisement::Version::V2, service_id));
|
||||
ScopedPtr<ConstPtr<ByteArray>> fast_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(
|
||||
BLEAdvertisement::Version::V2, BLEAdvertisement::SocketVersion::V2,
|
||||
service_id_hash.get(), scoped_advertisement_bytes.get()));
|
||||
if (fast_advertisement_bytes.isNull()) {
|
||||
// logger.atSevere().log("Failed to BLE advertise because we could not
|
||||
// create a fast advertisement for service UUID %s.",
|
||||
// fast_advertisement_service_uuid);
|
||||
|
||||
// We shouldn't have started an advertisement GATT server in the first
|
||||
// place if we are using fast advertisements. However, to avoid careless
|
||||
// leaks, try shutting down the server anyway.
|
||||
stopAdvertisementGattServer();
|
||||
return false;
|
||||
}
|
||||
advertisement->service_data.insert(std::make_pair(
|
||||
fast_advertisement_service_uuid, fast_advertisement_bytes.release()));
|
||||
scan_response->service_uuids.insert(fast_advertisement_service_uuid);
|
||||
}
|
||||
|
||||
if (!ble_medium_->startAdvertising(ConstifyPtr(advertisement.release()),
|
||||
ConstifyPtr(scan_response.release()),
|
||||
power_mode)) {
|
||||
// If BLE advertising was not successful, stop the advertisement GATT
|
||||
// server.
|
||||
stopAdvertisementGattServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// logger.atVerbose().flog("Started BLE advertising with advertisement %s for
|
||||
// serviceID %s.", advertisement_header, service_id);
|
||||
advertising_info_ = MakePtr(new AdvertisingInfo(service_id));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
ConstPtr<ByteArray> BLEV2<Platform>::createAdvertisementHeader(
|
||||
const string& service_id, ConstPtr<ByteArray> advertisement_bytes,
|
||||
bool is_fast_advertisement) {
|
||||
// Create a randomized dummy service ID to anonymize our header with.
|
||||
string dummy_service_id;
|
||||
dummy_service_id.reserve(kDummyServiceIdLength);
|
||||
for (int i = 0; i < kDummyServiceIdLength; i++) {
|
||||
dummy_service_id[i] = static_cast<char>(prng_->nextInt32() & 0x000000FF);
|
||||
}
|
||||
|
||||
// Put the service ID along with the dummy service ID into our bloom filter
|
||||
// Note: BloomFilter length should always match
|
||||
// BLEAdvertisementHeader::kServiceIdBloomFilterLength
|
||||
ScopedPtr<Ptr<BloomFilter<10>>> bloom_filter(new BloomFilter<10>());
|
||||
bloom_filter->add(dummy_service_id);
|
||||
|
||||
// Only add the service ID to our bloom filter if it's not a fast
|
||||
// advertisement. Fast advertisements want discoverers to avoid reading our
|
||||
// GATT advertisement.
|
||||
if (!is_fast_advertisement) {
|
||||
bloom_filter->add(service_id);
|
||||
}
|
||||
|
||||
// Create a hash seeded from dummy_service_id + advertisementBytes
|
||||
//
|
||||
// First, populate advertisement_bodies with the dummy_service_id and
|
||||
// advertisement_bytes.
|
||||
string advertisement_bodies;
|
||||
advertisement_bodies.reserve(dummy_service_id.size() +
|
||||
advertisement_bytes->size());
|
||||
advertisement_bodies.append(dummy_service_id.data(), dummy_service_id.size());
|
||||
advertisement_bodies.append(advertisement_bytes->getData(),
|
||||
advertisement_bytes->size());
|
||||
|
||||
// Then, generate the advertisement hash from the populated
|
||||
// advertisement_bodies string.
|
||||
ScopedPtr<ConstPtr<ByteArray>> advertisement_bodies_byte_array(MakeConstPtr(
|
||||
new ByteArray(advertisement_bodies.data(), advertisement_bodies.size())));
|
||||
ScopedPtr<ConstPtr<ByteArray>> advertisement_hash(
|
||||
generateAdvertisementHash(advertisement_bodies_byte_array.get()));
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> bloom_filter_bytes(bloom_filter->asBytes());
|
||||
string ble_advertisement_header_string = BLEAdvertisementHeader::asString(
|
||||
BLEAdvertisementHeader::Version::V2, kNumAdvertisementSlots,
|
||||
bloom_filter_bytes.get(), advertisement_hash.get());
|
||||
|
||||
return MakeConstPtr(new ByteArray(ble_advertisement_header_string.data(),
|
||||
ble_advertisement_header_string.size()));
|
||||
}
|
||||
|
||||
// Stops BLE advertising.
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::stopAdvertising() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!isAdvertising()) {
|
||||
// logger.atDebug().log("Can't turn off BLE advertising because it never
|
||||
// started.");
|
||||
return;
|
||||
}
|
||||
|
||||
ble_medium_->stopAdvertising();
|
||||
// Reset advertising_info_to mark that we're no longer advertising.
|
||||
advertising_info_.destroy();
|
||||
|
||||
// Do NOT stop the advertisement GATT server here. Doing so will cause any
|
||||
// other existing GATT connections to stop receiving callbacks. This affects
|
||||
// our BLE sockets. Therefore, we only stop it in shutdown() and
|
||||
// startAdvertising(), where it is safe to do so. At those two points, we
|
||||
// shouldn't expect any BLE sockets to be connected.
|
||||
|
||||
// logger.atVerbose().log("Turned BLE advertising off");
|
||||
}
|
||||
|
||||
// Returns true if currently scanning for BLE advertisements.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::isScanning() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return !scanning_info_.isNull();
|
||||
}
|
||||
|
||||
// Starts scanning for BLE advertisements (if it is possible for the device).
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::startScanning(
|
||||
const string& service_id,
|
||||
Ptr<DiscoveredPeripheralCallback> discovered_peripheral_callback,
|
||||
BLEMediumV2::PowerMode::Value power_mode,
|
||||
const string& fast_advertisement_service_uuid) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<DiscoveredPeripheralCallback>>
|
||||
scoped_discovered_peripheral_callback(discovered_peripheral_callback);
|
||||
|
||||
if (service_id.empty() || scoped_discovered_peripheral_callback.isNull()) {
|
||||
// logger.atSevere().log("Refusing to start BLE scanning because at least
|
||||
// one of workSource, serviceId, or discoveredPeripheralCallback is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isScanning()) {
|
||||
// logger.atSevere().log("Refusing to start BLE scanning because we are
|
||||
// already scanning.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// logger.atSevere().log("Can't start BLE scanning because Bluetooth was
|
||||
// never turned on");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// logger.atSevere().log("Can't start BLE scanning because BLE is not
|
||||
// available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
discovered_peripheral_tracker_->startTracking(
|
||||
service_id, scoped_discovered_peripheral_callback.release(),
|
||||
fast_advertisement_service_uuid);
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<ScanCallbackFacade>> scan_callback_facade(
|
||||
new ScanCallbackFacade(self_));
|
||||
std::set<string> service_uuids;
|
||||
service_uuids.insert(kCopresenceServiceUuid);
|
||||
if (!ble_medium_->startScanning(service_uuids, power_mode,
|
||||
scan_callback_facade.get())) {
|
||||
discovered_peripheral_tracker_->stopTracking(service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// logger.atVerbose().log("Started BLE scanning for serviceID %s.",
|
||||
// service_id);
|
||||
scanning_info_ = MakePtr(new ScanningInfo(
|
||||
service_id, scan_callback_facade.release(), createOnLostAlarm()));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::onAdvertisementFoundImpl(
|
||||
Ptr<BLEPeripheralV2> ble_peripheral,
|
||||
ConstPtr<BLEAdvertisementData> advertisement_data) {
|
||||
offloadFromPlatformThread(
|
||||
MakePtr(new ble_v2::OnAdvertisementFoundRunnable<Platform>(
|
||||
self_, ble_peripheral, advertisement_data)));
|
||||
}
|
||||
|
||||
// This method is synchronized because it affects class state, but is called
|
||||
// from a separate thread that has a recurring alarm running on it.
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::processOnLostTimeout() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
discovered_peripheral_tracker_->processLostGattAdvertisements();
|
||||
}
|
||||
|
||||
// Stops scanning for BLE advertisements.
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::stopScanning() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!isScanning()) {
|
||||
// logger.atDebug().log("Can't turn off BLE scanning because we never
|
||||
// started scanning.");
|
||||
return;
|
||||
}
|
||||
|
||||
scanning_info_->on_lost_alarm->cancel();
|
||||
|
||||
ble_medium_->stopScanning();
|
||||
discovered_peripheral_tracker_->stopTracking(scanning_info_->service_id);
|
||||
// Reset our bundle of scanning state to mark that we're no longer scanning.
|
||||
scanning_info_.destroy();
|
||||
}
|
||||
|
||||
// TODO(b/112199086) Change to RecurringCancelableAlarm
|
||||
template <typename Platform>
|
||||
Ptr<CancelableAlarm> BLEV2<Platform>::createOnLostAlarm() {
|
||||
return Ptr<CancelableAlarm>();
|
||||
}
|
||||
|
||||
// Returns true if the device is currently accepting incoming BLE socket
|
||||
// connections.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::isAcceptingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
return !accepting_connections_info_.isNull();
|
||||
}
|
||||
|
||||
// Starts accepting incoming BLE socket connections.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::startAcceptingConnections(
|
||||
const string& service_id,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<AcceptedConnectionCallback>>
|
||||
scoped_accepted_connection_callback(accepted_connection_callback);
|
||||
if (service_id.empty() || scoped_accepted_connection_callback.isNull()) {
|
||||
// logger.atSevere().log("Refusing to start accepting BLE connections
|
||||
// because at least one of serviceId or acceptedConnectionCallback is
|
||||
// null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAcceptingConnections()) {
|
||||
// logger.atSevere().log("Refusing to start accepting BLE connections for %s
|
||||
// because another BLE server socket is already in-progress.", service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// logger.atSevere().log("Can't start accepting BLE connections for %s
|
||||
// because Bluetooth isn't enabled.", service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// logger.atSevere().log("Can't start accepting BLE connections for %s
|
||||
// because BLE is not available.", service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(ahlee): Implement w/ the rest of the connecting logic.
|
||||
// Default to returning true and creating accepting_connections_info_ so we
|
||||
// can test the advertising and discovery flow fully.
|
||||
accepting_connections_info_ =
|
||||
MakePtr(new AcceptingConnectionsInfo(service_id));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stops accepting incoming BLE socket connections.
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::stopAcceptingConnections() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!isAcceptingConnections()) {
|
||||
// logger.atDebug().log("Can't stop accepting BLE connections because it was
|
||||
// never started.");
|
||||
return;
|
||||
}
|
||||
|
||||
ble_medium_->stopListeningForIncomingBLESockets();
|
||||
|
||||
// Reset our bundle of accepting connections state to mark that we're no
|
||||
// longer accepting connections.
|
||||
accepting_connections_info_.destroy();
|
||||
}
|
||||
|
||||
// Note: getGattConnectionBackoffPeriodMillis is only used in the java version
|
||||
// of reliablyConnect() for now.
|
||||
|
||||
// Returns true if the advertisement GATT server is currently running.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::isAdvertisementGattServerRunning() {
|
||||
return !gatt_server_info_.isNull();
|
||||
}
|
||||
|
||||
// Starts a GATT server to deliver additional advertisement data. Returns true
|
||||
// if the server was started successfully.
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::startAdvertisementGattServer(
|
||||
const string& service_id, ConstPtr<ByteArray> advertisement) {
|
||||
// advertisement is not being wrapped in a ScopedPtr because ownership is not
|
||||
// passed on from startAdvertising().
|
||||
|
||||
if (isAdvertisementGattServerRunning()) {
|
||||
// logger.atSevere().log("Refusing to start an advertisement GATT server
|
||||
// because one is already running.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a BleAdvertisement to wrap over the passed in advertisement.
|
||||
ScopedPtr<ConstPtr<ByteArray>> legacy_service_id_hash(
|
||||
generateServiceIdHash(BLEAdvertisement::Version::V1, service_id));
|
||||
ScopedPtr<ConstPtr<ByteArray>> legacy_ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(BLEAdvertisement::Version::V1,
|
||||
BLEAdvertisement::SocketVersion::V1,
|
||||
legacy_service_id_hash.get(), advertisement));
|
||||
if (legacy_ble_advertisement_bytes.isNull()) {
|
||||
// logger.atSevere().log("Refusing to start an advertisement GATT server
|
||||
// because creating a legacy BleAdvertisement with service ID %s failed.",
|
||||
// service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> service_id_hash(
|
||||
generateServiceIdHash(BLEAdvertisement::Version::V2, service_id));
|
||||
ScopedPtr<ConstPtr<ByteArray>> ble_advertisement_bytes(
|
||||
BLEAdvertisement::toBytes(BLEAdvertisement::Version::V2,
|
||||
BLEAdvertisement::SocketVersion::V2,
|
||||
service_id_hash.get(), advertisement));
|
||||
if (ble_advertisement_bytes.isNull()) {
|
||||
// logger.atSevere().log("Refusing to start an advertisement GATT server
|
||||
// because creating a BleAdvertisement with service ID %s failed.",
|
||||
// service_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return internalStartAdvertisementGattServer(
|
||||
legacy_ble_advertisement_bytes.release(),
|
||||
ble_advertisement_bytes.release());
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::internalStartAdvertisementGattServer(
|
||||
ConstPtr<ByteArray> legacy_ble_advertisement_bytes,
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes) {
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_legacy_ble_advertisement_bytes(
|
||||
legacy_ble_advertisement_bytes);
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_ble_advertisement_bytes(
|
||||
ble_advertisement_bytes);
|
||||
|
||||
ScopedPtr<Ptr<ServerGATTConnectionLifecycleCallbackFacade>>
|
||||
connection_lifecycle_callback(
|
||||
new ServerGATTConnectionLifecycleCallbackFacade(self_));
|
||||
ScopedPtr<Ptr<GATTServer>> gatt_server(
|
||||
ble_medium_->startGATTServer(connection_lifecycle_callback.get()));
|
||||
if (gatt_server.isNull()) {
|
||||
// logger.atSevere().withCause(e).log("Unable to start an advertisement GATT
|
||||
// server.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!generateAdvertisementCharacteristic(
|
||||
/* slot= */ 0, scoped_legacy_ble_advertisement_bytes.release(),
|
||||
gatt_server.get())) {
|
||||
gatt_server->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!generateAdvertisementCharacteristic(
|
||||
/* slot= */ 1, scoped_ble_advertisement_bytes.release(),
|
||||
gatt_server.get())) {
|
||||
gatt_server->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// GattCharacteristic is not included in GATTServerInfo because we don't need
|
||||
// it after it's been updated.
|
||||
gatt_server_info_ = MakePtr(new GATTServerInfo(
|
||||
gatt_server.release(), connection_lifecycle_callback.release()));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BLEV2<Platform>::generateAdvertisementCharacteristic(
|
||||
std::int32_t slot, ConstPtr<ByteArray> advertisement,
|
||||
Ptr<GATTServer> gatt_server) {
|
||||
// Avoid leaks.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_advertisement(advertisement);
|
||||
|
||||
std::set<GATTCharacteristic::Permission::Value> permissions;
|
||||
permissions.insert(GATTCharacteristic::Permission::READ);
|
||||
std::set<GATTCharacteristic::Property::Value> properties;
|
||||
properties.insert(GATTCharacteristic::Property::READ);
|
||||
Ptr<GATTCharacteristic> gatt_characteristic(gatt_server->createCharacteristic(
|
||||
kCopresenceServiceUuid, generateAdvertisementUuid(slot), permissions,
|
||||
properties));
|
||||
|
||||
if (gatt_characteristic.isNull()) {
|
||||
// logger.atSevere().withCause(e).log("Unable to create and add a
|
||||
// characterstic to the gatt server for the advertisement.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gatt_server->updateCharacteristic(gatt_characteristic,
|
||||
scoped_advertisement.release())) {
|
||||
// logger.atSevere().withCause(e).log("Unable to write a value to the GATT
|
||||
// characteristic.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: In the java counterpart this in a utils class.
|
||||
// Generates a characteristic UUID for an advertisement at the given slot.
|
||||
template <typename Platform>
|
||||
string BLEV2<Platform>::generateAdvertisementUuid(std::int32_t slot) {
|
||||
return UUID<Platform>(kAdvertisementUuidMsb, kAdvertisementUuidLsb | slot)
|
||||
.str();
|
||||
}
|
||||
|
||||
// Stops a GATT server used for additional advertisement data.
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::stopAdvertisementGattServer() {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (!isAdvertisementGattServerRunning()) {
|
||||
// logger.atSevere().log("Unable to stop the advertisement GATT server
|
||||
// because it's not running.");
|
||||
return;
|
||||
}
|
||||
|
||||
gatt_server_info_->gatt_server->stop();
|
||||
gatt_server_info_.destroy();
|
||||
}
|
||||
|
||||
// Connects to a GATT server, reads advertisement data, and then disconnects
|
||||
// from the GATT server. This method blocks until all advertisements are read,
|
||||
// or a connection error occurs.
|
||||
template <typename Platform>
|
||||
Ptr<AdvertisementReadResult<Platform>>
|
||||
BLEV2<Platform>::processFetchGattAdvertisementsRequest(
|
||||
Ptr<BLEPeripheralV2> peripheral, std::int32_t num_slots,
|
||||
Ptr<AdvertisementReadResult<Platform>> advertisement_read_result) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
if (advertisement_read_result.isNull()) {
|
||||
advertisement_read_result =
|
||||
MakeRefCountedPtr(new AdvertisementReadResult<Platform>());
|
||||
}
|
||||
|
||||
if (peripheral.isNull()) {
|
||||
// logger.atSevere().log("Can't read from an advertisement GATT server
|
||||
// because ble peripheral is null.");
|
||||
return advertisement_read_result;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// logger.atSevere().log("Can't read from an advertisement GATT server
|
||||
// because Bluetooth was never turned on.");
|
||||
return advertisement_read_result;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// logger.atSevere().log("Can't read from an advertisement GATT server
|
||||
// because BLE is not available.");
|
||||
return advertisement_read_result;
|
||||
}
|
||||
|
||||
return internalReadFromAdvertisementGattServer(peripheral, num_slots,
|
||||
advertisement_read_result);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<AdvertisementReadResult<Platform>>
|
||||
BLEV2<Platform>::internalReadFromAdvertisementGattServer(
|
||||
Ptr<BLEPeripheralV2> peripheral, std::int32_t num_slots,
|
||||
Ptr<AdvertisementReadResult<Platform>> advertisement_read_result) {
|
||||
// Attempt to connect and read some GATT characteristics.
|
||||
bool read_success = true;
|
||||
|
||||
ScopedPtr<Ptr<ClientGATTConnectionLifecycleCallbackFacade>>
|
||||
connection_lifecycle_callback(
|
||||
new ClientGATTConnectionLifecycleCallbackFacade(self_));
|
||||
ScopedPtr<Ptr<ClientGATTConnection>> gatt_connection(
|
||||
ble_medium_->connectToGATTServer(peripheral, kDefaultMtu,
|
||||
BLEMediumV2::PowerMode::HIGH,
|
||||
connection_lifecycle_callback.get()));
|
||||
if (!gatt_connection.isNull() && gatt_connection->discoverServices()) {
|
||||
// Read all advertisements from all slots that we haven't read from yet.
|
||||
for (std::int32_t slot = 0; slot < num_slots; ++slot) {
|
||||
// Make sure we haven't already read this advertisement before.
|
||||
if (advertisement_read_result->hasAdvertisement(slot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the characteristic even exists for this slot number. If the
|
||||
// characteristic doesn't exist, we shouldn't count the fetch as a
|
||||
// failure because there's nothing we could've done about a non-existent
|
||||
// characteristic.
|
||||
Ptr<GATTCharacteristic> gatt_characteristic(
|
||||
gatt_connection->getCharacteristic(kCopresenceServiceUuid,
|
||||
generateAdvertisementUuid(slot)));
|
||||
if (/* !advertisementSlotExists()= */ gatt_characteristic.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read advertisement data from the characteristic associated with this
|
||||
// slot.
|
||||
ScopedPtr<ConstPtr<ByteArray>> characteristic_value(
|
||||
gatt_connection->readCharacteristic(gatt_characteristic));
|
||||
if (!characteristic_value.isNull()) {
|
||||
advertisement_read_result->addAdvertisement(
|
||||
slot, characteristic_value.release());
|
||||
// logger.atVerbose().log("Successfully read advertisement at slot %d
|
||||
// on peripheral %s.", slot, peripheral);
|
||||
} else {
|
||||
// logger.atWarning().withCause(characteristicReadException).log("Can't
|
||||
// read advertisement for slot %d on peripheral %s.", slot,
|
||||
// peripheral);
|
||||
read_success = false;
|
||||
}
|
||||
// Whether or not we succeeded with this slot, we should try reading the
|
||||
// other slots to get as many advertisements as possible before
|
||||
// returning a success or failure.
|
||||
}
|
||||
|
||||
gatt_connection->disconnect();
|
||||
} else {
|
||||
// logger.atWarning().withCause(connectException).log("Can't connect to an
|
||||
// advertisement GATT server for peripheral %s.", peripheral);
|
||||
read_success = false;
|
||||
}
|
||||
|
||||
advertisement_read_result->recordLastReadStatus(read_success);
|
||||
return advertisement_read_result;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BLEV2<Platform>::offloadFromPlatformThread(Ptr<Runnable> runnable) {
|
||||
platform_thread_offloader_->execute(runnable);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
ConstPtr<ByteArray> BLEV2<Platform>::generateAdvertisementHash(
|
||||
ConstPtr<ByteArray> advertisement_bytes) {
|
||||
return Utils::sha256Hash(hash_utils_.get(), advertisement_bytes,
|
||||
BLEAdvertisementHeader::kAdvertisementHashLength);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
ConstPtr<ByteArray> BLEV2<Platform>::generateServiceIdHash(
|
||||
BLEAdvertisement::Version::Value version, const string& service_id) {
|
||||
ScopedPtr<ConstPtr<ByteArray>> service_id_bytes(
|
||||
MakeConstPtr(new ByteArray(service_id.data(), service_id.size())));
|
||||
switch (version) {
|
||||
case BLEAdvertisement::Version::V1:
|
||||
return Utils::legacySha256HashOnlyForPrinting(
|
||||
hash_utils_.get(), service_id_bytes.get(),
|
||||
BLEAdvertisement::kServiceIdHashLength);
|
||||
case BLEAdvertisement::Version::V2:
|
||||
// Fall through.
|
||||
case BLEAdvertisement::Version::UNKNOWN:
|
||||
// Fall through.
|
||||
default:
|
||||
// Use the latest known hashing scheme.
|
||||
return Utils::sha256Hash(hash_utils_.get(), service_id_bytes.get(),
|
||||
BLEAdvertisement::kServiceIdHashLength);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -1,313 +0,0 @@
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/internal/mediums/advertisement_read_result.h"
|
||||
#include "core/internal/mediums/ble_advertisement.h"
|
||||
#include "core/internal/mediums/bluetooth_radio.h"
|
||||
#include "core/internal/mediums/discovered_peripheral_callback.h"
|
||||
#include "core/internal/mediums/discovered_peripheral_tracker.h"
|
||||
#include "platform/api/ble_v2.h"
|
||||
#include "platform/api/bluetooth_adapter.h"
|
||||
#include "platform/api/hash_utils.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/cancelable_alarm.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/prng.h"
|
||||
#include "platform/ptr.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
namespace ble_v2 {
|
||||
|
||||
template <typename>
|
||||
class ProcessOnLostRunnable;
|
||||
|
||||
template <typename>
|
||||
class OnAdvertisementFoundRunnable;
|
||||
|
||||
} // namespace ble_v2
|
||||
|
||||
template <typename Platform>
|
||||
class BLEV2 {
|
||||
public:
|
||||
explicit BLEV2(Ptr<BluetoothRadio<Platform>> bluetooth_radio);
|
||||
~BLEV2();
|
||||
|
||||
bool isAvailable();
|
||||
// While the start* functions for each action (advertising, scanning,
|
||||
// accepting connections) take in a service_id, the stop* and is* functions do
|
||||
// not. This is because the service_id isn't used. In the java code, shutdown
|
||||
// calls all the stop* functions w/ a null service_id. The service_id is just
|
||||
// passed through to the corresponding is* function, which ignores it.
|
||||
// service_id should be added back in when C++ supports multi-client.
|
||||
bool startAdvertising(const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement,
|
||||
BLEMediumV2::PowerMode::Value power_mode,
|
||||
const string& fast_advertisement_service_uuid);
|
||||
void stopAdvertising();
|
||||
|
||||
bool startScanning(
|
||||
const string& service_id,
|
||||
Ptr<DiscoveredPeripheralCallback> discovered_peripheral_callback,
|
||||
BLEMediumV2::PowerMode::Value power_mode,
|
||||
const string& fast_advertisement_service_uuid);
|
||||
void stopScanning();
|
||||
|
||||
class AcceptedConnectionCallback {
|
||||
public:
|
||||
virtual ~AcceptedConnectionCallback() {}
|
||||
|
||||
// TODO(ahlee): Add in connecting logic.
|
||||
};
|
||||
|
||||
bool isAcceptingConnections();
|
||||
bool startAcceptingConnections(
|
||||
const string& service_id,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback);
|
||||
void stopAcceptingConnections();
|
||||
|
||||
private:
|
||||
template <typename>
|
||||
friend class ble_v2::ProcessOnLostRunnable;
|
||||
template <typename>
|
||||
friend class ble_v2::OnAdvertisementFoundRunnable;
|
||||
|
||||
class GATTAdvertisementFetcherFacade
|
||||
: public DiscoveredPeripheralTracker<Platform>::GattAdvertisementFetcher {
|
||||
public:
|
||||
explicit GATTAdvertisementFetcherFacade(Ptr<BLEV2<Platform>> impl)
|
||||
: impl_(impl) {}
|
||||
~GATTAdvertisementFetcherFacade() override {}
|
||||
|
||||
Ptr<AdvertisementReadResult<Platform>> fetchGattAdvertisements(
|
||||
Ptr<BLEPeripheralV2> ble_peripheral, std::int32_t num_slots,
|
||||
Ptr<AdvertisementReadResult<Platform>> advertisement_read_result)
|
||||
override {
|
||||
return impl_->processFetchGattAdvertisementsRequest(
|
||||
ble_peripheral, num_slots, advertisement_read_result);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> impl_;
|
||||
};
|
||||
|
||||
class ScanCallbackFacade : public BLEMediumV2::ScanCallback {
|
||||
public:
|
||||
explicit ScanCallbackFacade(Ptr<BLEV2<Platform>> impl) : impl_(impl) {}
|
||||
~ScanCallbackFacade() override {}
|
||||
|
||||
void onAdvertisementFound(
|
||||
Ptr<BLEPeripheralV2> peripheral,
|
||||
ConstPtr<BLEAdvertisementData> advertisement_data) override {
|
||||
impl_->onAdvertisementFoundImpl(peripheral, advertisement_data);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> impl_;
|
||||
};
|
||||
|
||||
class ClientGATTConnectionLifecycleCallbackFacade
|
||||
: public ClientGATTConnectionLifecycleCallback {
|
||||
public:
|
||||
explicit ClientGATTConnectionLifecycleCallbackFacade(
|
||||
Ptr<BLEV2<Platform>> impl)
|
||||
: impl_(impl) {}
|
||||
~ClientGATTConnectionLifecycleCallbackFacade() override {}
|
||||
|
||||
void onDisconnected(Ptr<ClientGATTConnection> connection) override {
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<ClientGATTConnection>> scoped_connection(connection);
|
||||
|
||||
// Nothing else to do for now.
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> impl_;
|
||||
};
|
||||
|
||||
class ServerGATTConnectionLifecycleCallbackFacade
|
||||
: public ServerGATTConnectionLifecycleCallback {
|
||||
public:
|
||||
explicit ServerGATTConnectionLifecycleCallbackFacade(
|
||||
Ptr<BLEV2<Platform>> impl)
|
||||
: impl_(impl) {}
|
||||
~ServerGATTConnectionLifecycleCallbackFacade() override {}
|
||||
|
||||
void onCharacteristicSubscription(
|
||||
Ptr<ServerGATTConnection> connection,
|
||||
Ptr<GATTCharacteristic> characteristic) override {
|
||||
// Avoid leaks. Do not scope the characteristic because it is ref counted
|
||||
// by the per-platform ble_v2 implementation.
|
||||
ScopedPtr<Ptr<ServerGATTConnection>> scoped_connection(connection);
|
||||
|
||||
// Nothing else to do for now.
|
||||
}
|
||||
|
||||
void onCharacteristicUnsubscription(
|
||||
Ptr<ServerGATTConnection> connection,
|
||||
Ptr<GATTCharacteristic> characteristic) override {
|
||||
// Avoid leaks. Do not scope the characteristic because it is ref counted
|
||||
// by the per-platform ble_v2 implementation.
|
||||
ScopedPtr<Ptr<ServerGATTConnection>> scoped_connection(connection);
|
||||
|
||||
// Nothing else to do for now.
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<BLEV2<Platform>> impl_;
|
||||
};
|
||||
|
||||
struct ScanningInfo {
|
||||
ScanningInfo(const string& service_id,
|
||||
Ptr<ScanCallbackFacade> scan_callback_facade,
|
||||
Ptr<CancelableAlarm> on_lost_alarm)
|
||||
: service_id(service_id),
|
||||
scan_callback_facade(scan_callback_facade),
|
||||
on_lost_alarm(on_lost_alarm) {}
|
||||
~ScanningInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
}
|
||||
|
||||
const string service_id;
|
||||
ScopedPtr<Ptr<ScanCallbackFacade>> scan_callback_facade;
|
||||
// TODO(ahlee): Change to recurring cancelable alarm
|
||||
ScopedPtr<Ptr<CancelableAlarm>> on_lost_alarm;
|
||||
};
|
||||
|
||||
struct AdvertisingInfo {
|
||||
explicit AdvertisingInfo(const string& service_id)
|
||||
: service_id(service_id) {}
|
||||
~AdvertisingInfo() {}
|
||||
|
||||
const string service_id;
|
||||
};
|
||||
|
||||
struct GATTServerInfo {
|
||||
GATTServerInfo(Ptr<GATTServer> gatt_server,
|
||||
Ptr<ServerGATTConnectionLifecycleCallbackFacade>
|
||||
connection_lifecycle_callback)
|
||||
: gatt_server(gatt_server),
|
||||
connection_lifecycle_callback(connection_lifecycle_callback) {}
|
||||
~GATTServerInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
}
|
||||
|
||||
ScopedPtr<Ptr<GATTServer>> gatt_server;
|
||||
ScopedPtr<Ptr<ServerGATTConnectionLifecycleCallbackFacade>>
|
||||
connection_lifecycle_callback;
|
||||
};
|
||||
|
||||
struct AcceptingConnectionsInfo {
|
||||
explicit AcceptingConnectionsInfo(const string& service_id)
|
||||
: service_id(service_id) {}
|
||||
~AcceptingConnectionsInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
}
|
||||
|
||||
const string service_id;
|
||||
// TODO(ahlee): Fill in.
|
||||
};
|
||||
|
||||
static const std::int32_t kNumAdvertisementSlots;
|
||||
static const std::int32_t kMaxAdvertisementLength;
|
||||
static const std::int32_t kDummyServiceIdLength;
|
||||
static const char* kCopresenceServiceUuid;
|
||||
static const std::int64_t kOnLostTimeoutMillis;
|
||||
static const std::int64_t kGattAdvertisementOperationTimeoutMillis;
|
||||
static const std::int64_t kMinConnectionAttemptRecoveryDurationMillis;
|
||||
static const std::int32_t kMaxConnectionAttemptRecoveryFuzzDurationMillis;
|
||||
static const std::uint32_t kDefaultMtu;
|
||||
static const std::int64_t kAdvertisementUuidMsb;
|
||||
static const std::int64_t kAdvertisementUuidLsb;
|
||||
|
||||
bool isAdvertising();
|
||||
ConstPtr<ByteArray> createAdvertisementHeader(
|
||||
const string& service_id, ConstPtr<ByteArray> advertisement_bytes,
|
||||
bool is_fast_advertisement);
|
||||
|
||||
bool isScanning();
|
||||
void onAdvertisementFoundImpl(
|
||||
Ptr<BLEPeripheralV2> ble_peripheral,
|
||||
ConstPtr<BLEAdvertisementData> advertisement_data);
|
||||
void processOnLostTimeout();
|
||||
Ptr<CancelableAlarm> createOnLostAlarm();
|
||||
|
||||
bool isAdvertisementGattServerRunning();
|
||||
bool startAdvertisementGattServer(const string& service_id,
|
||||
ConstPtr<ByteArray> advertisement);
|
||||
bool internalStartAdvertisementGattServer(
|
||||
ConstPtr<ByteArray> legacy_ble_advertisement_bytes,
|
||||
ConstPtr<ByteArray> ble_advertisement_bytes);
|
||||
bool generateAdvertisementCharacteristic(
|
||||
std::int32_t slot, ConstPtr<ByteArray> advertisement,
|
||||
Ptr<GATTServer> gatt_server);
|
||||
void stopAdvertisementGattServer();
|
||||
|
||||
Ptr<AdvertisementReadResult<Platform>> processFetchGattAdvertisementsRequest(
|
||||
Ptr<BLEPeripheralV2> peripheral, std::int32_t num_slots,
|
||||
Ptr<AdvertisementReadResult<Platform>> advertisement_read_result);
|
||||
Ptr<AdvertisementReadResult<Platform>>
|
||||
internalReadFromAdvertisementGattServer(
|
||||
Ptr<BLEPeripheralV2> ble_peripheral, std::int32_t num_slots,
|
||||
Ptr<AdvertisementReadResult<Platform>> advertisement_read_result);
|
||||
|
||||
void offloadFromPlatformThread(Ptr<Runnable> runnable);
|
||||
// TODO(ahlee): Move these out to utils (also used by
|
||||
// DiscoveredPeripheralTracker).
|
||||
ConstPtr<ByteArray> generateAdvertisementHash(
|
||||
ConstPtr<ByteArray> advertisement_bytes);
|
||||
ConstPtr<ByteArray> generateServiceIdHash(
|
||||
BLEAdvertisement::Version::Value version, const string& service_id);
|
||||
|
||||
// This maps to a helper function found in bluetoothlowenergy/Utils.java. In
|
||||
// the C++ code we moved it because it's only used here.
|
||||
string generateAdvertisementUuid(std::int32_t slot);
|
||||
|
||||
// ------------ GENERAL ------------
|
||||
|
||||
ScopedPtr<Ptr<Lock>> lock_;
|
||||
// Where we throw potentially blocking work off of the platform thread.
|
||||
ScopedPtr<Ptr<typename Platform::SingleThreadExecutorType>>
|
||||
platform_thread_offloader_;
|
||||
ScopedPtr<Ptr<Prng>> prng_;
|
||||
ScopedPtr<Ptr<HashUtils>> hash_utils_;
|
||||
|
||||
// ------------ CORE BLE ------------
|
||||
|
||||
Ptr<BluetoothRadio<Platform>> bluetooth_radio_;
|
||||
ScopedPtr<Ptr<BluetoothAdapter>> bluetooth_adapter_;
|
||||
// The underlying, per-platform implementation.
|
||||
ScopedPtr<Ptr<BLEMediumV2>> ble_medium_;
|
||||
|
||||
// ------------ DISCOVERY ------------
|
||||
|
||||
// scanning_info_ is not scoped because it's nullable.
|
||||
Ptr<ScanningInfo> scanning_info_;
|
||||
ScopedPtr<Ptr<DiscoveredPeripheralTracker<Platform>>>
|
||||
discovered_peripheral_tracker_;
|
||||
ScopedPtr<Ptr<typename Platform::ScheduledExecutorType>> on_lost_executor_;
|
||||
|
||||
// ------------ ADVERTISING ------------
|
||||
|
||||
// advertising_info_, gatt_server_info_, and accepting_connections_info_ are
|
||||
// not scoped because they are nullable.
|
||||
Ptr<AdvertisingInfo> advertising_info_;
|
||||
Ptr<GATTServerInfo> gatt_server_info_;
|
||||
Ptr<AcceptingConnectionsInfo> accepting_connections_info_;
|
||||
std::shared_ptr<BLEV2> self_{this, [](void*){}};
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/mediums/ble_v2.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_H_
|
||||
@@ -15,14 +15,14 @@ cc_library(
|
||||
"discovered_peripheral_callback.h",
|
||||
],
|
||||
visibility = [
|
||||
"//core_v2/internal:__subpackages__",
|
||||
"//core/internal:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//core_v2:core_types",
|
||||
"//platform_v2/base",
|
||||
"//platform_v2/base:util",
|
||||
"//platform_v2/public:logging",
|
||||
"//platform_v2/public:types",
|
||||
"//core:core_types",
|
||||
"//platform/base",
|
||||
"//platform/base:util",
|
||||
"//platform/public:logging",
|
||||
"//platform/public:types",
|
||||
"//absl/container:flat_hash_map",
|
||||
"//absl/container:flat_hash_set",
|
||||
"//absl/strings",
|
||||
@@ -41,8 +41,8 @@ cc_test(
|
||||
],
|
||||
deps = [
|
||||
":ble_v2",
|
||||
"//platform_v2/base",
|
||||
"//platform_v2/impl/g3", # buildcleaner: keep
|
||||
"//platform/base",
|
||||
"//platform/impl/g3", # buildcleaner: keep
|
||||
"//testing/base/public:gunit_main",
|
||||
"//absl/time",
|
||||
],
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/advertisement_read_result.h"
|
||||
#include "core/internal/mediums/ble_v2/advertisement_read_result.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "platform_v2/public/mutex_lock.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform_v2/public/mutex.h"
|
||||
#include "platform_v2/public/system_clock.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/time/clock.h"
|
||||
@@ -87,4 +87,4 @@ class AdvertisementReadResult {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_ADVERTISEMENT_READ_RESULT_H_
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/advertisement_read_result.h"
|
||||
#include "core/internal/mediums/ble_v2/advertisement_read_result.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_advertisement.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_advertisement.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "platform_v2/base/base_input_stream.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "platform/base/base_input_stream.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -122,4 +122,4 @@ class BleAdvertisement {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_H_
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_advertisement_header.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_advertisement_header.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "platform_v2/base/base64_utils.h"
|
||||
#include "platform_v2/base/base_input_stream.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "platform/base/base_input_stream.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -80,4 +80,4 @@ class BleAdvertisementHeader {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_advertisement_header.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_advertisement_header.h"
|
||||
|
||||
#include "platform_v2/base/base64_utils.h"
|
||||
#include "platform/base/base64_utils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_advertisement.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_advertisement.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_packet.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_packet.h"
|
||||
|
||||
#include "platform_v2/base/base_input_stream.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "platform/base/base_input_stream.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -47,4 +47,4 @@ class BlePacket {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PACKET_H_
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_packet.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_packet.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -32,4 +32,4 @@ class BlePeripheral {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_PERIPHERAL_H_
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_peripheral.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_peripheral.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
#define CORE_V2_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
|
||||
#include "core_v2/internal/mediums/ble_v2/ble_peripheral.h"
|
||||
#include "core_v2/listeners.h"
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "core/internal/mediums/ble_v2/ble_peripheral.h"
|
||||
#include "core/listeners.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -28,4 +28,4 @@ struct DiscoveredPeripheralCallback {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_CALLBACK_H_
|
||||
@@ -9,31 +9,19 @@ namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
const std::int32_t BloomFilter<CapacityInBytes>::kHasherNumberOfRepetitions = 5;
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
BloomFilter<CapacityInBytes>::BloomFilter() : bits_() {}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
BloomFilter<CapacityInBytes>::BloomFilter(ConstPtr<ByteArray> bytes) : bits_() {
|
||||
const char* bytes_read_ptr = bytes->getData();
|
||||
for (size_t byte_index = 0; byte_index < bytes->size(); byte_index++) {
|
||||
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);
|
||||
bits_->Set((byte_index * 8) + bit_index,
|
||||
(*bytes_read_ptr >> bit_index) & 0x01);
|
||||
}
|
||||
bytes_read_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
BloomFilter<CapacityInBytes>::~BloomFilter() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
ConstPtr<ByteArray> BloomFilter<CapacityInBytes>::asBytes() {
|
||||
BloomFilterBase::operator ByteArray() const {
|
||||
// Gets a binary string representation of the bitset where the leftmost
|
||||
// character corresponds to bitset position (total size) - 1.
|
||||
//
|
||||
@@ -41,13 +29,13 @@ ConstPtr<ByteArray> BloomFilter<CapacityInBytes>::asBytes() {
|
||||
// [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_.to_string();
|
||||
std::string bitset_binary_string = bits_->ToString();
|
||||
|
||||
Ptr<ByteArray> result_bytes{new ByteArray{CapacityInBytes}};
|
||||
char* result_bytes_write_ptr = result_bytes->getData();
|
||||
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) {
|
||||
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,
|
||||
@@ -55,35 +43,29 @@ ConstPtr<ByteArray> BloomFilter<CapacityInBytes>::asBytes() {
|
||||
*result_bytes_write_ptr = static_cast<char>(byte_value & 0x000000FF);
|
||||
result_bytes_write_ptr++;
|
||||
}
|
||||
return ConstifyPtr(result_bytes);
|
||||
return result_bytes;
|
||||
}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
void BloomFilter<CapacityInBytes>::add(const std::string& s) {
|
||||
std::vector<std::int32_t> hashes = getHashes(s);
|
||||
for (std::vector<std::int32_t>::iterator it = hashes.begin();
|
||||
it != hashes.end(); ++it) {
|
||||
size_t position = static_cast<size_t>(*it) % bits_.size();
|
||||
bits_.set(position);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
bool BloomFilter<CapacityInBytes>::possiblyContains(const std::string& s) {
|
||||
std::vector<std::int32_t> hashes = getHashes(s);
|
||||
for (std::vector<std::int32_t>::iterator i = hashes.begin();
|
||||
i != hashes.end(); ++i) {
|
||||
size_t position = static_cast<size_t>(*i) % bits_.size();
|
||||
if (!bits_.test(position)) {
|
||||
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;
|
||||
}
|
||||
|
||||
template <size_t CapacityInBytes>
|
||||
std::vector<std::int32_t> BloomFilter<CapacityInBytes>::getHashes(
|
||||
const std::string& s) {
|
||||
std::vector<std::int32_t> BloomFilterBase::GetHashes(const std::string& s) {
|
||||
std::vector<std::int32_t> hashes(kHasherNumberOfRepetitions, 0);
|
||||
|
||||
absl::uint128 hash128;
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
#define CORE_INTERNAL_MEDIUMS_BLOOM_FILTER_H_
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
@@ -23,25 +20,63 @@ namespace mediums {
|
||||
* the bit set to ensure the bit set's length is a multiple of 8 (and can
|
||||
* neatly be returned as a ByteArray).
|
||||
*/
|
||||
template <size_t CapacityInBytes>
|
||||
class BloomFilter {
|
||||
class BloomFilterBase {
|
||||
public:
|
||||
BloomFilter();
|
||||
explicit BloomFilter(ConstPtr<ByteArray> bytes);
|
||||
~BloomFilter();
|
||||
explicit operator ByteArray() const;
|
||||
|
||||
ConstPtr<ByteArray> asBytes();
|
||||
void Add(const std::string& s);
|
||||
bool PossiblyContains(const std::string& s);
|
||||
|
||||
void add(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;
|
||||
};
|
||||
|
||||
bool possiblyContains(const std::string& s);
|
||||
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:
|
||||
static const std::int32_t kHasherNumberOfRepetitions;
|
||||
int GetMinBytesForBits() const { return (bits_->Size() + 7) >> 3; }
|
||||
|
||||
std::vector<std::int32_t> getHashes(const std::string& s);
|
||||
BitSet* bits_;
|
||||
};
|
||||
|
||||
std::bitset<CapacityInBytes * 8> 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
|
||||
@@ -49,6 +84,4 @@ class BloomFilter {
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/mediums/bloom_filter.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLOOM_FILTER_H_
|
||||
|
||||
@@ -10,68 +10,102 @@ namespace connections {
|
||||
namespace mediums {
|
||||
namespace {
|
||||
|
||||
const size_t kByteArrayLength = 100;
|
||||
constexpr size_t kByteArrayLength = 100;
|
||||
|
||||
TEST(BloomFilterTest, EmptyFilterReturnsEmptyArray) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_bloom_filter_bytes(
|
||||
scoped_bloom_filter->asBytes());
|
||||
ByteArray bloom_filter_bytes(bloom_filter);
|
||||
std::string empty_string(kByteArrayLength, '\0');
|
||||
ASSERT_EQ(0, memcmp(scoped_bloom_filter_bytes->getData(), empty_string.data(),
|
||||
empty_string.size()));
|
||||
|
||||
EXPECT_EQ(empty_string, std::string(bloom_filter_bytes));
|
||||
}
|
||||
|
||||
TEST(BloomFilterTest, EmptyFilterNeverContains) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_1"));
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_2"));
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_3"));
|
||||
EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1"));
|
||||
EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_2"));
|
||||
EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_3"));
|
||||
}
|
||||
|
||||
TEST(BloomFilterTest, AddSuccess) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_1"));
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
ASSERT_TRUE(scoped_bloom_filter->possiblyContains("ELEMENT_1"));
|
||||
EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1"));
|
||||
|
||||
bloom_filter.Add("ELEMENT_1");
|
||||
|
||||
EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_1"));
|
||||
}
|
||||
|
||||
TEST(BloomFilterTest, AddOnlyGivenArg) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
ASSERT_TRUE(scoped_bloom_filter->possiblyContains("ELEMENT_1"));
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_2"));
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_3"));
|
||||
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) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
scoped_bloom_filter->add("ELEMENT_2");
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
ASSERT_TRUE(scoped_bloom_filter->possiblyContains("ELEMENT_1"));
|
||||
ASSERT_TRUE(scoped_bloom_filter->possiblyContains("ELEMENT_2"));
|
||||
ASSERT_FALSE(scoped_bloom_filter->possiblyContains("ELEMENT_3"));
|
||||
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) {
|
||||
ScopedPtr<Ptr<BloomFilter<10>>> scoped_bloom_filter(new BloomFilter<10>());
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
scoped_bloom_filter->add("ELEMENT_2");
|
||||
scoped_bloom_filter->add("ELEMENT_3");
|
||||
BloomFilter<10> bloom_filter;
|
||||
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_bloom_filter_bytes(
|
||||
scoped_bloom_filter->asBytes());
|
||||
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');
|
||||
ASSERT_NE(scoped_bloom_filter_bytes->asString(), empty_string);
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,10 +120,10 @@ TEST(BloomFilterTest, AddMultipleArgsReturnsNonemptyArray) {
|
||||
* something like [ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, ..., 1, 0].
|
||||
*/
|
||||
TEST(BloomFilterTest, RandomnessNoEndBias) {
|
||||
ScopedPtr<Ptr<BloomFilter<kByteArrayLength>>> scoped_bloom_filter(
|
||||
new BloomFilter<kByteArrayLength>());
|
||||
BloomFilter<kByteArrayLength> bloom_filter;
|
||||
|
||||
// Add one element to our BloomFilter.
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
bloom_filter.Add("ELEMENT_1");
|
||||
|
||||
std::int32_t non_zero_count = 0;
|
||||
std::int32_t longest_zero_streak = 0;
|
||||
@@ -98,11 +132,9 @@ TEST(BloomFilterTest, RandomnessNoEndBias) {
|
||||
// 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.
|
||||
ScopedPtr<ConstPtr<ByteArray>> scoped_bloom_filter_bytes(
|
||||
scoped_bloom_filter->asBytes());
|
||||
const char* bloom_filter_bytes_read_ptr =
|
||||
scoped_bloom_filter_bytes->getData();
|
||||
for (int i = 0; i < scoped_bloom_filter_bytes->size(); i++) {
|
||||
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 {
|
||||
@@ -127,31 +159,31 @@ TEST(BloomFilterTest, RandomnessNoEndBias) {
|
||||
// kByteArrayLength - one end of the array.
|
||||
std::int32_t longest_acceptable_zero_streak =
|
||||
kByteArrayLength - (kByteArrayLength / non_zero_count);
|
||||
ASSERT_TRUE(longest_zero_streak <= longest_acceptable_zero_streak);
|
||||
|
||||
EXPECT_TRUE(longest_zero_streak <= longest_acceptable_zero_streak);
|
||||
}
|
||||
|
||||
TEST(BloomFilterTest, RandomnessFalsePositiveRate) {
|
||||
ScopedPtr<Ptr<BloomFilter<10>>> scoped_bloom_filter(new BloomFilter<10>());
|
||||
BloomFilter<10> bloom_filter;
|
||||
|
||||
// Add 5 distinct elements to the BloomFilter.
|
||||
scoped_bloom_filter->add("ELEMENT_1");
|
||||
scoped_bloom_filter->add("ELEMENT_2");
|
||||
scoped_bloom_filter->add("ELEMENT_3");
|
||||
scoped_bloom_filter->add("ELEMENT_4");
|
||||
scoped_bloom_filter->add("ELEMENT_5");
|
||||
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 +=
|
||||
scoped_bloom_filter->possiblyContains("ELEMENT_" + std::to_string(i))
|
||||
? 1
|
||||
: 0;
|
||||
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%.
|
||||
ASSERT_LE(false_positives, 5);
|
||||
EXPECT_LE(false_positives, 5);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,466 +1,386 @@
|
||||
#include "core/internal/mediums/bluetooth_classic.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "core/internal/mediums/uuid.h"
|
||||
#include "platform/synchronized.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/mutex_lock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
const std::int32_t BluetoothClassic<Platform>::kMaxConcurrentAcceptLoops = 5;
|
||||
BluetoothClassic::BluetoothClassic(BluetoothRadio& radio) : radio_(radio) {}
|
||||
|
||||
template <typename Platform>
|
||||
BluetoothClassic<Platform>::BluetoothClassic(
|
||||
Ptr<BluetoothRadio<Platform>> bluetooth_radio)
|
||||
: lock_(Platform::createLock()),
|
||||
bluetooth_radio_(bluetooth_radio),
|
||||
bluetooth_adapter_(Platform::createBluetoothAdapter()),
|
||||
bluetooth_classic_medium_(Platform::createBluetoothClassicMedium()),
|
||||
scan_info_(),
|
||||
original_scan_mode_(BluetoothAdapter::ScanMode::UNKNOWN),
|
||||
original_device_name_(),
|
||||
accept_loops_thread_pool_(
|
||||
Platform::createMultiThreadExecutor(kMaxConcurrentAcceptLoops)),
|
||||
bluetooth_server_sockets_() {}
|
||||
|
||||
template <typename Platform>
|
||||
BluetoothClassic<Platform>::~BluetoothClassic() {
|
||||
stopDiscovery();
|
||||
for (BluetoothServerSocketMap::iterator it =
|
||||
bluetooth_server_sockets_.begin();
|
||||
it != bluetooth_server_sockets_.end(); ++it) {
|
||||
stopAcceptingConnections(it->first);
|
||||
BluetoothClassic::~BluetoothClassic() {
|
||||
// Destructor is not taking locks, but methods it is calling are.
|
||||
StopDiscovery();
|
||||
while (!server_sockets_.empty()) {
|
||||
StopAcceptingConnections(server_sockets_.begin()->first);
|
||||
}
|
||||
turnOffDiscoverability();
|
||||
TurnOffDiscoverability();
|
||||
|
||||
// All the AcceptLoopRunnable objects in here should already have gotten an
|
||||
// opportunity to shut themselves down cleanly in the calls to
|
||||
// stopAcceptingConnections() above.
|
||||
accept_loops_thread_pool_->shutdown();
|
||||
|
||||
original_device_name_.destroy();
|
||||
scan_info_.destroy();
|
||||
// StopAcceptingConnections() above.
|
||||
accept_loops_runner_.Shutdown();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::isAvailable() {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::IsAvailable() const {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return !bluetooth_classic_medium_.isNull() && !bluetooth_adapter_.isNull();
|
||||
return IsAvailableLocked();
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::turnOnDiscoverability(
|
||||
const string& device_name) {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::IsAvailableLocked() const {
|
||||
return medium_.IsValid() && adapter_.IsValid();
|
||||
}
|
||||
|
||||
bool BluetoothClassic::TurnOnDiscoverability(const std::string& device_name) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (device_name.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Refusing to turn on Bluetooth
|
||||
// discoverability because a null deviceName was passed in.");
|
||||
NEARBY_LOG(INFO,
|
||||
"Refusing to turn on BT discoverability. Empty device name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't turn on Bluetooth discoverability
|
||||
// because Bluetooth isn't enabled.");
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOG(INFO, "Can't turn on BT discoverability. BT is off.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't turn on Bluetooth discoverability
|
||||
// because Bluetooth isn't available.");
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOG(INFO, "Can't turn on BT discoverability. BT is not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDiscoverable()) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to turn on Bluetooth
|
||||
// discoverability with device name %s because we're already discoverable
|
||||
// with device name %s.", deviceName, bluetoothAdapter.getName());
|
||||
if (IsDiscoverable()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Refusing to turn on BT discoverability; new name='%s'; "
|
||||
"current name='%s'",
|
||||
device_name.c_str(), adapter_.GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!modifyDeviceName(device_name)) {
|
||||
// TODO(reznor): log.atSevere().log("Failed to turn on Bluetooth
|
||||
// discoverability because we couldn't set the device name to %s",
|
||||
// deviceName);
|
||||
if (!ModifyDeviceName(device_name)) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Failed to turn on BT discoverability; "
|
||||
"failed to set name to %s",
|
||||
device_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!modifyScanMode(BluetoothAdapter::ScanMode::CONNECTABLE_DISCOVERABLE)) {
|
||||
// TODO(reznor): log.atSevere().log("Failed to turn on Bluetooth
|
||||
// discoverability because we couldn't set the scan mode to %d",
|
||||
// BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
if (!ModifyScanMode(ScanMode::kConnectableDiscoverable)) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Failed to turn on BT discoverability; "
|
||||
"failed to set scan_mode to %d",
|
||||
ScanMode::kConnectableDiscoverable);
|
||||
|
||||
// Don't forget to perform this rollback of the partial state changes we've
|
||||
// made til now.
|
||||
restoreDeviceName();
|
||||
RestoreDeviceName();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(reznor): log.atVerbose().log("Turned on Bluetooth discoverability with
|
||||
// deviceName %s", deviceName);
|
||||
NEARBY_LOG(INFO, "Turned on BT discoverability with device_name=%s",
|
||||
device_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BluetoothClassic<Platform>::turnOffDiscoverability() {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::TurnOffDiscoverability() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isDiscoverable()) {
|
||||
// TODO(reznor): log.atDebug().log("Can't turn off Bluetooth discoverability
|
||||
// because it was never turned on.");
|
||||
return;
|
||||
if (!IsDiscoverable()) {
|
||||
NEARBY_LOG(INFO, "Can't turn off BT discoverability; it is already off");
|
||||
return false;
|
||||
}
|
||||
|
||||
restoreScanMode();
|
||||
restoreDeviceName();
|
||||
RestoreScanMode();
|
||||
RestoreDeviceName();
|
||||
|
||||
// TODO(reznor): log.atVerbose().log("Turned Bluetooth discoverability off");
|
||||
NEARBY_LOG(INFO, "Turned Bluetooth discoverability off");
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::isDiscoverable() const {
|
||||
return ((!original_device_name_.isNull()) &&
|
||||
(BluetoothAdapter::ScanMode::CONNECTABLE_DISCOVERABLE ==
|
||||
bluetooth_adapter_->getScanMode()));
|
||||
bool BluetoothClassic::IsDiscoverable() const {
|
||||
return (!original_device_name_.empty() &&
|
||||
(adapter_.GetScanMode() == ScanMode::kConnectableDiscoverable));
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::modifyDeviceName(const string& device_name) {
|
||||
original_device_name_ = bluetooth_adapter_->getName();
|
||||
bool BluetoothClassic::ModifyDeviceName(const std::string& device_name) {
|
||||
if (original_device_name_.empty()) {
|
||||
original_device_name_ = adapter_.GetName();
|
||||
}
|
||||
|
||||
if (!bluetooth_adapter_->setName(device_name)) {
|
||||
original_device_name_.destroy();
|
||||
return adapter_.SetName(device_name);
|
||||
}
|
||||
|
||||
bool BluetoothClassic::ModifyScanMode(ScanMode scan_mode) {
|
||||
if (original_scan_mode_ == ScanMode::kUnknown) {
|
||||
original_scan_mode_ = adapter_.GetScanMode();
|
||||
}
|
||||
|
||||
if (!adapter_.SetScanMode(scan_mode)) {
|
||||
original_scan_mode_ = ScanMode::kUnknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::modifyScanMode(
|
||||
BluetoothAdapter::ScanMode::Value scan_mode) {
|
||||
original_scan_mode_ = bluetooth_adapter_->getScanMode();
|
||||
|
||||
if (!bluetooth_adapter_->setScanMode(scan_mode)) {
|
||||
original_scan_mode_ = BluetoothAdapter::ScanMode::UNKNOWN;
|
||||
bool BluetoothClassic::RestoreScanMode() {
|
||||
if (original_scan_mode_ == ScanMode::kUnknown ||
|
||||
!adapter_.SetScanMode(original_scan_mode_)) {
|
||||
NEARBY_LOG(INFO, "Failed to restore original Bluetooth scan mode to %d",
|
||||
original_scan_mode_);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BluetoothClassic<Platform>::restoreScanMode() {
|
||||
if (!bluetooth_adapter_->setScanMode(original_scan_mode_)) {
|
||||
// TODO(reznor): log.atWarning().log("Failed to restore original Bluetooth
|
||||
// scan mode to %d", originalScanMode);
|
||||
}
|
||||
|
||||
// Regardless of whether or not we could actually restore the Bluetooth scan
|
||||
// mode, reset our relevant state.
|
||||
original_scan_mode_ = BluetoothAdapter::ScanMode::UNKNOWN;
|
||||
original_scan_mode_ = ScanMode::kUnknown;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BluetoothClassic<Platform>::restoreDeviceName() {
|
||||
if (!bluetooth_adapter_->setName(*original_device_name_)) {
|
||||
// TODO(reznor): log.atWarning().log("Failed to restore original Bluetooth
|
||||
// device name to %s", originalDeviceName);
|
||||
bool BluetoothClassic::RestoreDeviceName() {
|
||||
if (original_device_name_.empty() ||
|
||||
!adapter_.SetName(original_device_name_)) {
|
||||
NEARBY_LOG(INFO, "Failed to restore original Bluetooth device name to %s",
|
||||
original_device_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Regardless of whether or not we could actually restore the Bluetooth device
|
||||
// name, reset the marker that opens us up for business for the next time
|
||||
// 'round.
|
||||
original_device_name_.destroy();
|
||||
original_device_name_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::startDiscovery(
|
||||
Ptr<DiscoveredDeviceCallback> discovered_device_callback) {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::StartDiscovery(DiscoveredDeviceCallback callback) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (discovered_device_callback.isNull()) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to start discovery of
|
||||
// Bluetooth devices because a null discoveredDeviceCallback was passed
|
||||
// in.");
|
||||
return false;
|
||||
}
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<DiscoveredDeviceCallback>> scoped_discovered_device_callback(
|
||||
discovered_device_callback);
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't discover Bluetooth devices
|
||||
// because Bluetooth isn't enabled.");
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOG(INFO, "Can't discover BT devices because BT isn't enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't discover Bluetooth devices
|
||||
// because Bluetooth isn't available.");
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOG(INFO, "Can't discover BT devices because BT isn't available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDiscovering()) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to start discovery of
|
||||
// Bluetooth devices because another discovery is already in-progress.");
|
||||
if (IsDiscovering()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Refusing to start discovery of BT devices because another "
|
||||
"discovery is already in-progress.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<BluetoothDiscoveryCallback>>
|
||||
scoped_bluetooth_discovery_callback(new BluetoothDiscoveryCallback(
|
||||
scoped_discovered_device_callback.get()));
|
||||
|
||||
if (!bluetooth_classic_medium_->startDiscovery(
|
||||
scoped_bluetooth_discovery_callback.get())) {
|
||||
// TODO(reznor): log.atSevere().log("Failed to start discovery of Bluetooth
|
||||
// devices.");
|
||||
if (!medium_.StartDiscovery(callback)) {
|
||||
NEARBY_LOG(INFO, "Failed to start discovery of BT devices.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark the fact that we're currently performing a Bluetooth scan.
|
||||
scan_info_ =
|
||||
MakePtr(new ScanInfo(scoped_discovered_device_callback.release(),
|
||||
scoped_bluetooth_discovery_callback.release()));
|
||||
scan_info_.valid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BluetoothClassic<Platform>::stopDiscovery() {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::StopDiscovery() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (!isDiscovering()) {
|
||||
// TODO(reznor): log.atDebug().log("Can't stop discovery of Bluetooth
|
||||
// devices because it never started.");
|
||||
return;
|
||||
if (!IsDiscovering()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Can't stop discovery of BT devices because it never started.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_classic_medium_->stopDiscovery()) {
|
||||
// TODO(reznor): log.atWarning().log("Failed to stop discovery of Bluetooth
|
||||
// devices.");
|
||||
if (!medium_.StopDiscovery()) {
|
||||
NEARBY_LOG(INFO, "Failed to stop discovery of Bluetooth devices.");
|
||||
return false;
|
||||
}
|
||||
// Regardless of whether or not stopDiscovery() succeeded, destroy scan_info_
|
||||
// to:
|
||||
//
|
||||
// a) Avoid a leak.
|
||||
// b) Mark the fact that we're no longer performing a Bluetooth discovery.
|
||||
scan_info_.destroy();
|
||||
|
||||
scan_info_.valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::isDiscovering() const {
|
||||
return !scan_info_.isNull();
|
||||
}
|
||||
bool BluetoothClassic::IsDiscovering() const { return scan_info_.valid; }
|
||||
|
||||
template <typename Platform>
|
||||
class AcceptLoopRunnable : public Runnable {
|
||||
public:
|
||||
AcceptLoopRunnable(
|
||||
Ptr<typename BluetoothClassic<Platform>::AcceptedConnectionCallback>
|
||||
accepted_connection_callback,
|
||||
Ptr<BluetoothServerSocket> listening_socket, const string& service_name)
|
||||
: accepted_connection_callback_(accepted_connection_callback),
|
||||
listening_socket_(listening_socket),
|
||||
service_name_(service_name) {}
|
||||
bool BluetoothClassic::StartAcceptingConnections(
|
||||
const std::string& service_name, AcceptedConnectionCallback callback) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
void run() override {
|
||||
while (true) {
|
||||
ExceptionOr<Ptr<BluetoothSocket>> bluetooth_socket =
|
||||
listening_socket_->accept();
|
||||
if (!bluetooth_socket.ok()) {
|
||||
if (Exception::IO == bluetooth_socket.exception()) {
|
||||
Utils::closeSocket(listening_socket_, "Bluetooth", service_name_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
accepted_connection_callback_->onConnectionAccepted(
|
||||
bluetooth_socket.result());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPtr<
|
||||
Ptr<typename BluetoothClassic<Platform>::AcceptedConnectionCallback>>
|
||||
accepted_connection_callback_;
|
||||
Ptr<BluetoothServerSocket> listening_socket_;
|
||||
const string service_name_;
|
||||
};
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::startAcceptingConnections(
|
||||
const string& service_name,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback) {
|
||||
Synchronized s(lock_.get());
|
||||
|
||||
// Avoid leaks.
|
||||
ScopedPtr<Ptr<AcceptedConnectionCallback>>
|
||||
scoped_accepted_connection_callback(accepted_connection_callback);
|
||||
if (scoped_accepted_connection_callback.isNull() || service_name.empty()) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to start accepting Bluetooth
|
||||
// connections because at least one of serviceName or
|
||||
// acceptedConnectionCallback is null.");
|
||||
if (service_name.empty()) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Refusing to start accepting BT connections; service name is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't create Bluetooth server socket
|
||||
// for %s because Bluetooth isn't enabled.", serviceName);
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Can't create BT server socket [service=%s]; BT is disabled.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't start accepting BLuetooth
|
||||
// connections for %s because Bluetooth isn't available.", serviceName);
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Can't start accepting BT connections [service=%s]; BT not available.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAcceptingConnections(service_name)) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to start accepting Bluetooth
|
||||
// connections for %s because a Bluetooth server is already in-progress for
|
||||
// that service name.", serviceName);
|
||||
if (IsAcceptingConnectionsLocked(service_name)) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Refusing to start accepting BT connections [service=%s]; BT "
|
||||
"server is already in-progress with the same name.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ExceptionOr<Ptr<BluetoothServerSocket>> listening_socket =
|
||||
bluetooth_classic_medium_->listenForService(
|
||||
service_name, generateUUIDFromString(service_name));
|
||||
if (!listening_socket.ok()) {
|
||||
if (Exception::IO == listening_socket.exception()) {
|
||||
// TODO(reznor): log.atSevere().withCause(e).log("Failed to start
|
||||
// accepting Bluetooth connections for %s.", serviceName);
|
||||
return false;
|
||||
}
|
||||
BluetoothServerSocket socket = medium_.ListenForService(
|
||||
service_name, GenerateUuidFromString(service_name));
|
||||
if (!socket.IsValid()) {
|
||||
NEARBY_LOG(INFO, "Failed to start accepting Bluetooth connections for %s.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start the accept loop on a dedicated thread - this stays alive and
|
||||
// listening for new incoming connections until stopAcceptingConnections() is
|
||||
// invoked.
|
||||
accept_loops_thread_pool_->execute(MakePtr(new AcceptLoopRunnable<Platform>(
|
||||
scoped_accepted_connection_callback.release(), listening_socket.result(),
|
||||
service_name)));
|
||||
|
||||
// Mark the fact that there's an in-progress Bluetooth server accepting
|
||||
// connections.
|
||||
bluetooth_server_sockets_.insert(
|
||||
std::make_pair(service_name, listening_socket.result()));
|
||||
auto owned_socket =
|
||||
server_sockets_.emplace(service_name, std::move(socket)).first->second;
|
||||
|
||||
// Start the accept loop on a dedicated thread - this stays alive and
|
||||
// listening for new incoming connections until StopAcceptingConnections() is
|
||||
// invoked.
|
||||
accept_loops_runner_.Execute([callback = std::move(callback),
|
||||
server_socket = std::move(owned_socket),
|
||||
service_name]() mutable {
|
||||
while (true) {
|
||||
BluetoothSocket client_socket = server_socket.Accept();
|
||||
if (!client_socket.IsValid()) {
|
||||
server_socket.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
callback.accepted_cb(std::move(client_socket));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
bool BluetoothClassic<Platform>::isAcceptingConnections(
|
||||
const string& service_name) {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::IsAcceptingConnections(const std::string& service_name) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
return bluetooth_server_sockets_.find(service_name) !=
|
||||
bluetooth_server_sockets_.end();
|
||||
return IsAcceptingConnectionsLocked(service_name);
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
void BluetoothClassic<Platform>::stopAcceptingConnections(
|
||||
const string& service_name) {
|
||||
Synchronized s(lock_.get());
|
||||
bool BluetoothClassic::IsAcceptingConnectionsLocked(
|
||||
const std::string& service_name) {
|
||||
return server_sockets_.find(service_name) != server_sockets_.end();
|
||||
}
|
||||
|
||||
bool BluetoothClassic::StopAcceptingConnections(
|
||||
const std::string& service_name) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (service_name.empty()) {
|
||||
// TODO(ahlee): logger.atSevere().log("Unable to stop accepting Bluetooth
|
||||
// connections because the serviceName is empty.");
|
||||
return;
|
||||
NEARBY_LOG(INFO,
|
||||
"Unable to stop accepting BT connections because the "
|
||||
"service_name is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAcceptingConnections(service_name)) {
|
||||
// TODO(reznor): log.atDebug().log("Can't stop accepting Bluetooth
|
||||
// connections for %s because it was never started.", serviceName);
|
||||
return;
|
||||
const auto& it = server_sockets_.find(service_name);
|
||||
if (it == server_sockets_.end()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Can't stop accepting BT connections for %s because it was "
|
||||
"never started.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Closing the BluetoothServerSocket will kick off the suicide of the thread
|
||||
// in accept_loops_thread_pool_ that blocks on BluetoothServerSocket.accept().
|
||||
// That may take some time to complete, but there's no particular reason to
|
||||
// wait around for it.
|
||||
BluetoothServerSocketMap::iterator listening_socket_iter =
|
||||
bluetooth_server_sockets_.find(service_name);
|
||||
auto item = server_sockets_.extract(it);
|
||||
|
||||
// Store a handle to the BluetoothServerSocket, so we can use it after
|
||||
// removing the entry from bluetooth_server_sockets_; making it scoped
|
||||
// removing the entry from server_sockets_; making it scoped
|
||||
// is a bonus that takes care of deallocation before we leave this method.
|
||||
ScopedPtr<Ptr<BluetoothServerSocket>> scoped_listening_socket(
|
||||
listening_socket_iter->second);
|
||||
BluetoothServerSocket& listening_socket = item.mapped();
|
||||
|
||||
// Regardless of whether or not we fail to close the existing
|
||||
// BluetoothServerSocket, remove it from bluetooth_server_sockets_ so that it
|
||||
// BluetoothServerSocket, remove it from server_sockets_ so that it
|
||||
// frees up this service for another round.
|
||||
bluetooth_server_sockets_.erase(listening_socket_iter);
|
||||
|
||||
// Finally, close the BluetoothServerSocket.
|
||||
Exception::Value e = scoped_listening_socket->close();
|
||||
if (Exception::NONE != e) {
|
||||
if (Exception::IO == e) {
|
||||
// TODO(reznor): log.atSevere().withCause(e).log("Failed to close
|
||||
// Bluetooth server socket for %s.", serviceName);
|
||||
}
|
||||
if (!listening_socket.Close().Ok()) {
|
||||
NEARBY_LOG(INFO, "Failed to close BT server socket for %s.",
|
||||
service_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
Ptr<BluetoothSocket> BluetoothClassic<Platform>::connect(
|
||||
Ptr<BluetoothDevice> bluetooth_device, const string& service_name) {
|
||||
Synchronized s(lock_.get());
|
||||
BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device,
|
||||
const std::string& service_name) {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOG(INFO, "BluetoothClassic::Connect: device=%p", &bluetooth_device);
|
||||
// Socket to return. To allow for NRVO to work, it has to be a single object.
|
||||
BluetoothSocket socket;
|
||||
|
||||
if (bluetooth_device.isNull() || service_name.empty()) {
|
||||
// TODO(reznor): log.atSevere().log("Refusing to create client Bluetooth
|
||||
// socket because at least one of bluetoothDevice or serviceName is null.");
|
||||
return Ptr<BluetoothSocket>();
|
||||
if (service_name.empty()) {
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"Refusing to create client BT socket because service_name is empty.");
|
||||
return socket;
|
||||
}
|
||||
|
||||
if (!bluetooth_radio_->isEnabled()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't create client Bluetooth socket to
|
||||
// %s because Bluetooth isn't enabled.", bluetoothSocketName);
|
||||
return Ptr<BluetoothSocket>();
|
||||
if (!radio_.IsEnabled()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"Can't create client BT socket [service=%s]: BT isn't enabled.",
|
||||
service_name.c_str());
|
||||
return socket;
|
||||
}
|
||||
|
||||
if (!isAvailable()) {
|
||||
// TODO(reznor): log.atSevere().log("Can't create client Bluetooth socket to
|
||||
// %s because Bluetooth isn't available.", bluetoothSocketName);
|
||||
return Ptr<BluetoothSocket>();
|
||||
if (!IsAvailableLocked()) {
|
||||
NEARBY_LOG(
|
||||
INFO, "Can't create client BT socket [service=%s]; BT isn't available.",
|
||||
service_name.c_str());
|
||||
return socket;
|
||||
}
|
||||
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// This block deviates from the corresponding Java code.
|
||||
//
|
||||
// In Java, we pause an in-progress discovery before attempting this
|
||||
// connection, and then resume it after, but the memory management of the
|
||||
// DiscoveredDeviceCallback is complicated in C++, and would need a severe
|
||||
// deviation from the Java code, so we're choosing the lesser of 2 evils, and
|
||||
// introducing this (simplifying) deviation instead -- also, this deviation is
|
||||
// fairly inconsequential since we don't yet have a use-case that needs a
|
||||
// device that:
|
||||
//
|
||||
// a) uses the C++ code,
|
||||
// b) has Bluetooth Classic support, and
|
||||
// c) plays the role of Discoverer.
|
||||
ExceptionOr<Ptr<BluetoothSocket>> bluetooth_socket =
|
||||
bluetooth_classic_medium_->connectToService(
|
||||
bluetooth_device, generateUUIDFromString(service_name));
|
||||
if (!bluetooth_socket.ok()) {
|
||||
if (Exception::IO == bluetooth_socket.exception()) {
|
||||
// TODO(reznor): log.atSevere().log("Failed to connect via Bluetooth
|
||||
// socket to %s.", bluetoothSocketName);
|
||||
}
|
||||
return Ptr<BluetoothSocket>();
|
||||
socket = medium_.ConnectToService(bluetooth_device,
|
||||
GenerateUuidFromString(service_name));
|
||||
if (!socket.IsValid()) {
|
||||
NEARBY_LOG(INFO, "Failed to Connect via BT [service=%s]",
|
||||
service_name.c_str());
|
||||
}
|
||||
|
||||
return bluetooth_socket.result();
|
||||
return socket;
|
||||
}
|
||||
|
||||
template <typename Platform>
|
||||
string BluetoothClassic<Platform>::generateUUIDFromString(const string& data) {
|
||||
return UUID<Platform>(data).str();
|
||||
BluetoothDevice BluetoothClassic::GetRemoteDevice(
|
||||
const std::string& mac_address) {
|
||||
MutexLock lock(&mutex_);
|
||||
return medium_.GetRemoteDevice(mac_address);
|
||||
}
|
||||
|
||||
std::string BluetoothClassic::GetMacAddress() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return medium_.GetMacAddress();
|
||||
}
|
||||
|
||||
std::string BluetoothClassic::GenerateUuidFromString(const std::string& data) {
|
||||
return std::string(Uuid(data));
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -2,168 +2,182 @@
|
||||
#define CORE_INTERNAL_MEDIUMS_BLUETOOTH_CLASSIC_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "core/internal/mediums/bluetooth_radio.h"
|
||||
#include "core/internal/mediums/utils.h"
|
||||
#include "platform/api/bluetooth_adapter.h"
|
||||
#include "platform/api/bluetooth_classic.h"
|
||||
#include "platform/api/lock.h"
|
||||
#include "platform/api/multi_thread_executor.h"
|
||||
#include "platform/byte_array.h"
|
||||
#include "platform/port/string.h"
|
||||
#include "platform/ptr.h"
|
||||
#include "platform/runnable.h"
|
||||
#include "core/listeners.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/public/bluetooth_adapter.h"
|
||||
#include "platform/public/bluetooth_classic.h"
|
||||
#include "platform/public/multi_thread_executor.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
template <typename Platform>
|
||||
class BluetoothClassic {
|
||||
public:
|
||||
explicit BluetoothClassic(Ptr<BluetoothRadio<Platform>> bluetooth_radio);
|
||||
~BluetoothClassic();
|
||||
|
||||
bool isAvailable();
|
||||
|
||||
bool turnOnDiscoverability(const string& device_name);
|
||||
void turnOffDiscoverability();
|
||||
|
||||
// Callback that is invoked when a nearby Bluetooth device is discovered.
|
||||
class DiscoveredDeviceCallback {
|
||||
public:
|
||||
virtual ~DiscoveredDeviceCallback() {}
|
||||
|
||||
virtual void onDeviceDiscovered(Ptr<BluetoothDevice> device) = 0;
|
||||
virtual void onDeviceNameChanged(Ptr<BluetoothDevice> device) = 0;
|
||||
virtual void onDeviceLost(Ptr<BluetoothDevice> device) = 0;
|
||||
};
|
||||
|
||||
bool startDiscovery(Ptr<DiscoveredDeviceCallback> discovered_device_callback);
|
||||
void stopDiscovery();
|
||||
using DiscoveredDeviceCallback = BluetoothClassicMedium::DiscoveryCallback;
|
||||
using ScanMode = BluetoothAdapter::ScanMode;
|
||||
|
||||
// Callback that is invoked when a new connection is accepted.
|
||||
class AcceptedConnectionCallback {
|
||||
public:
|
||||
virtual ~AcceptedConnectionCallback() {}
|
||||
|
||||
virtual void onConnectionAccepted(Ptr<BluetoothSocket> socket) = 0;
|
||||
struct AcceptedConnectionCallback {
|
||||
std::function<void(BluetoothSocket socket)> accepted_cb =
|
||||
DefaultCallback<BluetoothSocket>();
|
||||
};
|
||||
|
||||
bool startAcceptingConnections(
|
||||
const string& service_name,
|
||||
Ptr<AcceptedConnectionCallback> accepted_connection_callback);
|
||||
bool isAcceptingConnections(const string& service_name);
|
||||
void stopAcceptingConnections(const string& service_name);
|
||||
explicit BluetoothClassic(BluetoothRadio& bluetooth_radio);
|
||||
~BluetoothClassic();
|
||||
|
||||
Ptr<BluetoothSocket> connect(Ptr<BluetoothDevice> bluetooth_device,
|
||||
const string& service_name);
|
||||
// Returns true, if BT communications are supported by a platform.
|
||||
bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Sets custom device name, and then enables BT discoverable mode.
|
||||
// Returns true, if name and scan mode are successfully set, and false
|
||||
// otherwise.
|
||||
// Called by server.
|
||||
bool TurnOnDiscoverability(const std::string& device_name)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Disables BT discoverability, and restores scan mode and device name to
|
||||
// what they were before the call to TurnOnDiscoverability().
|
||||
// Returns false if no successful call TurnOnDiscoverability() was previously
|
||||
// made, otherwise returns true.
|
||||
// Called by server.
|
||||
bool TurnOffDiscoverability() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Enables BT discovery mode. Will report any discoverable devices in range
|
||||
// through a callback.
|
||||
// Returns true, if discovery mode was enabled, false otherwise.
|
||||
// Called by client.
|
||||
bool StartDiscovery(DiscoveredDeviceCallback callback)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Disables BT discovery mode.
|
||||
// Returns true, if discovery mode was previously enabled, false otherwise.
|
||||
// Called by client.
|
||||
bool StopDiscovery() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Starts a worker thread, creates a BT server socket, associates it with a
|
||||
// service name; in a worker thread repeatedly calls ServerSocket::Accept().
|
||||
// Any connected sockets returned from Accept() are passed to a callback.
|
||||
// Returns true, if server socket was successfully created, false otherwise.
|
||||
// Called by server.
|
||||
bool StartAcceptingConnections(const std::string& service_name,
|
||||
AcceptedConnectionCallback callback)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true, if object is currently running a Accept() loop.
|
||||
bool IsAcceptingConnections(const std::string& service_name)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Closes server socket corresponding to a service name. This automatically
|
||||
// terminates Accept() loop, if it were running.
|
||||
// Called by server.
|
||||
bool StopAcceptingConnections(const std::string& service_name)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if this object owns a valid platform implementation.
|
||||
bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock lock(&mutex_);
|
||||
return medium_.IsValid();
|
||||
}
|
||||
|
||||
// Returns true if this object has a valid BluetoothAdapter reference.
|
||||
bool IsAdapterValid() const ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock lock(&mutex_);
|
||||
return adapter_.IsValid();
|
||||
}
|
||||
|
||||
// Establishes connection to BT service that was might be started on another
|
||||
// device with StartAcceptingConnections() using the same service_name.
|
||||
// Blocks until connection is established, or server-side is terminated.
|
||||
// Returns socket instance. On success, BluetoothSocket.IsValid() return true.
|
||||
// Called by client.
|
||||
BluetoothSocket Connect(BluetoothDevice& bluetooth_device,
|
||||
const std::string& service_name)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
std::string GetMacAddress() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
BluetoothDevice GetRemoteDevice(const std::string& mac_address)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
class BluetoothDiscoveryCallback
|
||||
: public BluetoothClassicMedium::DiscoveryCallback {
|
||||
public:
|
||||
explicit BluetoothDiscoveryCallback(
|
||||
Ptr<DiscoveredDeviceCallback> discovered_device_callback)
|
||||
: discovered_device_callback_(discovered_device_callback) {}
|
||||
~BluetoothDiscoveryCallback() override {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
void onDeviceDiscovered(Ptr<BluetoothDevice> bluetooth_device) override {
|
||||
discovered_device_callback_->onDeviceDiscovered(bluetooth_device);
|
||||
}
|
||||
void onDeviceNameChanged(Ptr<BluetoothDevice> bluetooth_device) override {
|
||||
discovered_device_callback_->onDeviceNameChanged(bluetooth_device);
|
||||
}
|
||||
void onDeviceLost(Ptr<BluetoothDevice> bluetooth_device) override {
|
||||
discovered_device_callback_->onDeviceLost(bluetooth_device);
|
||||
}
|
||||
|
||||
private:
|
||||
// This could well have been a ScopedPtr, with BluetoothDiscoveryCallback in
|
||||
// turn being owned by ScanInfo (and it would have been cleaner overall,
|
||||
// since the chain of wrapped callbacks starting from
|
||||
// BluetoothDiscoveryCallback would then destruct like a stack of dominoes
|
||||
// falling, triggered by the destruction of ScanInfo), but we instead give
|
||||
// ownership of this DiscoveredDeviceCallback *and*
|
||||
// BluetoothDiscoveryCallback to ScanInfo, to maintain compatibility with
|
||||
// the Java code.
|
||||
Ptr<DiscoveredDeviceCallback> discovered_device_callback_;
|
||||
};
|
||||
|
||||
struct ScanInfo {
|
||||
ScanInfo(Ptr<DiscoveredDeviceCallback> discovered_device_callback,
|
||||
Ptr<BluetoothDiscoveryCallback> bluetooth_discovery_callback)
|
||||
: discovered_device_callback(discovered_device_callback),
|
||||
bluetooth_discovery_callback(bluetooth_discovery_callback) {}
|
||||
~ScanInfo() {
|
||||
// Nothing to do (the ScopedPtr members take care of themselves).
|
||||
}
|
||||
|
||||
// Stores the DiscoveredDeviceCallback passed in to startDiscovery() by
|
||||
// clients so that we can internally stop and start Bluetooth scans
|
||||
// transparently as needed (for example, when a call to connect() is
|
||||
// invoked).
|
||||
ScopedPtr<Ptr<DiscoveredDeviceCallback>> discovered_device_callback;
|
||||
// The ordering of bluetooth_discovery_callback_ coming after
|
||||
// discovered_device_callback_ is very deliberate --
|
||||
// bluetooth_discovery_callback_ contains a reference to
|
||||
// discovered_device_callback_, so it should be destroyed first.
|
||||
ScopedPtr<Ptr<BluetoothDiscoveryCallback>> bluetooth_discovery_callback;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
static string generateUUIDFromString(const string& data);
|
||||
static constexpr int kMaxConcurrentAcceptLoops = 5;
|
||||
|
||||
static const std::int32_t kMaxConcurrentAcceptLoops;
|
||||
// Constructs UUID object from arbitrary string, using MD5 hash, and then
|
||||
// converts UUID to a readable UUID string and returns it.
|
||||
static std::string GenerateUuidFromString(const std::string& data);
|
||||
|
||||
bool isDiscoverable() const;
|
||||
bool modifyDeviceName(const string& device_name);
|
||||
bool modifyScanMode(BluetoothAdapter::ScanMode::Value scan_mode);
|
||||
void restoreScanMode();
|
||||
void restoreDeviceName();
|
||||
bool isDiscovering() const;
|
||||
// Same as IsAvailable(), but must be called with mutex_ held.
|
||||
bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// ------------ GENERAL ------------
|
||||
// Same as IsAcceptingConnections(), but must be called with mutex_ held.
|
||||
bool IsAcceptingConnectionsLocked(const std::string& service_name)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
ScopedPtr<Ptr<Lock>> lock_;
|
||||
// Returns true, if discoverability is enabled with TurnOnDiscoverability().
|
||||
bool IsDiscoverable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// ------------ CORE BLUETOOTH ------------
|
||||
// Assignes a different name to BT adapter.
|
||||
// Returns true if successful. Stores original device name.
|
||||
bool ModifyDeviceName(const std::string& device_name)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Ptr<BluetoothRadio<Platform>> bluetooth_radio_;
|
||||
ScopedPtr<Ptr<BluetoothAdapter>> bluetooth_adapter_;
|
||||
// The underlying, per-platform implementation.
|
||||
ScopedPtr<Ptr<BluetoothClassicMedium>> bluetooth_classic_medium_;
|
||||
// Changes current scan mode. This is an implementation of
|
||||
// Turn<On/Off>Discoveradility() method. Stores original scan mode.
|
||||
bool ModifyScanMode(ScanMode scan_mode) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// ------------ DISCOVERY ------------
|
||||
// Restores original device name (the one before the very first call to
|
||||
// ModifyDeviceName()). Returns true if successful.
|
||||
bool RestoreScanMode() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Restores original device scan mode (the one before the very first call to
|
||||
// ModifyScanMode()). Returns true if successful.
|
||||
bool RestoreDeviceName() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if device is currently in discovery mode.
|
||||
bool IsDiscovering() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
mutable Mutex mutex_;
|
||||
BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_);
|
||||
BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){
|
||||
radio_.GetBluetoothAdapter()};
|
||||
BluetoothClassicMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_};
|
||||
|
||||
// A bundle of state required to do a Bluetooth Classic scan. When non-null,
|
||||
// we are currently performing a Bluetooth scan.
|
||||
Ptr<ScanInfo> scan_info_;
|
||||
|
||||
// ------------ ADVERTISING ------------
|
||||
ScanInfo scan_info_ ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// The original scan mode (that controls visibility to scanners) of the device
|
||||
// before we modified it. Restored when we stop advertising.
|
||||
BluetoothAdapter::ScanMode::Value original_scan_mode_;
|
||||
// The original Bluetooth device name, before we modified it. If non-null, we
|
||||
ScanMode original_scan_mode_ ABSL_GUARDED_BY(mutex_) = ScanMode::kUnknown;
|
||||
|
||||
// The original Bluetooth device name, before we modified it. If non-empty, we
|
||||
// are currently Bluetooth discoverable. Restored when we stop advertising.
|
||||
Ptr<string> original_device_name_;
|
||||
std::string original_device_name_ ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// A thread pool dedicated to running all the accept loops from
|
||||
// startAcceptingConnections().
|
||||
ScopedPtr<Ptr<typename Platform::MultiThreadExecutorType>>
|
||||
accept_loops_thread_pool_;
|
||||
// A map of service name -> ServerSocket. While this map is non-empty, we
|
||||
// StartAcceptingConnections().
|
||||
MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops};
|
||||
|
||||
// A map of service Name -> ServerSocket. If map is non-empty, we
|
||||
// are currently listening for incoming connections.
|
||||
typedef std::map<string, Ptr<BluetoothServerSocket>> BluetoothServerSocketMap;
|
||||
BluetoothServerSocketMap bluetooth_server_sockets_;
|
||||
// BluetoothServerSocket instances are used from accept_loops_runner_,
|
||||
// and thus require pointer stability.
|
||||
absl::flat_hash_map<std::string, BluetoothServerSocket> server_sockets_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#include "core/internal/mediums/bluetooth_classic.cc"
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLUETOOTH_CLASSIC_H_
|
||||
|
||||
+7
-7
@@ -1,13 +1,13 @@
|
||||
#include "core_v2/internal/mediums/bluetooth_classic.h"
|
||||
#include "core/internal/mediums/bluetooth_classic.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_v2/internal/mediums/bluetooth_radio.h"
|
||||
#include "platform_v2/base/medium_environment.h"
|
||||
#include "platform_v2/public/bluetooth_classic.h"
|
||||
#include "platform_v2/public/count_down_latch.h"
|
||||
#include "platform_v2/public/logging.h"
|
||||
#include "platform_v2/public/system_clock.h"
|
||||
#include "core/internal/mediums/bluetooth_radio.h"
|
||||
#include "platform/base/medium_environment.h"
|
||||
#include "platform/public/bluetooth_classic.h"
|
||||
#include "platform/public/count_down_latch.h"
|
||||
#include "platform/public/logging.h"
|
||||
#include "platform/public/system_clock.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/time.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user