Release based on cl/313536507.

Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I83ec7dee1a7ef6f4bdfd47482c94d03910525b7e
This commit is contained in:
Alexey Polyudov
2020-06-04 10:06:31 -07:00
parent c3a89bb894
commit 7e19ffbab7
321 changed files with 23019 additions and 1376 deletions
+3 -1
View File
@@ -63,7 +63,9 @@ cc_library(
":types",
"//platform:types",
"//platform:utils",
"//platform/impl/sample",
"//platform/api",
"//platform/impl/g3",
"//platform/impl/shared/sample:sample_wifi_medium",
"//platform/port:string",
],
)
+3 -2
View File
@@ -62,8 +62,9 @@ target_link_libraries(core_build_test
absl::strings
core
core_types
platform_impl_default_lock
platform_impl_sample
platform_impl_g3
platform_impl_shared_posix_lock
platform_impl_shared_sample
platform_port_string
platform_types
platform_utils
+5 -3
View File
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include "core/core.h"
@@ -20,9 +19,10 @@
#include "core/params.h"
#include "core/payload.h"
#include "core/status.h"
#include "platform/api/platform.h"
#include "platform/byte_array.h"
#include "platform/file_impl.h"
#include "platform/impl/sample/sample_platform.h"
#include "platform/impl/shared/sample/sample_wifi_medium.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
@@ -30,6 +30,8 @@ namespace location {
namespace nearby {
namespace connections {
using TestPlatform = platform::ImplementationPlatform;
class ResultListenerImpl : public ResultListener {
public:
void onResult(Status::Value status) override {}
@@ -67,7 +69,7 @@ class PayloadListenerImpl : public PayloadListener {
};
void check_compilation() {
Core<sample::SamplePlatform> core;
Core<TestPlatform> core;
const string name = "name";
const string service_id = "service_id";
+12 -7
View File
@@ -46,15 +46,20 @@ namespace connections {
* SystemClock
* ConditionVariable
*
* The Platform class must also provide typedefs for the following subset of
* primitives to identify the concrete classes:
* A sample Platform definitions can be found at
* //platform/impl/shared/sample/sample_platform.cc
*
* SingleThreadExecutorType
* MultiThreadExecutorType
* ScheduledExecutorType
* 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.
*
* A sample Platform class can be found at
* //platform/impl/sample/sample_platform.h
* 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>
class Core {
+33 -8
View File
@@ -15,38 +15,39 @@
cc_library(
name = "internal",
srcs = [
"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_endpoint_channel.cc",
"wifi_lan_service_info.cc",
],
hdrs = [
"bandwidth_upgrade_handler.h",
"bandwidth_upgrade_manager.cc",
"bandwidth_upgrade_manager.h",
"base_bandwidth_upgrade_handler.cc",
"base_bandwidth_upgrade_handler.h",
"base_endpoint_channel.cc",
"base_endpoint_channel.h",
"base_pcp_handler.cc",
"base_pcp_handler.h",
"ble_advertisement.h",
"ble_compat.h",
"ble_endpoint_channel.cc",
"ble_endpoint_channel.h",
"bluetooth_device_name.h",
"bluetooth_endpoint_channel.cc",
"bluetooth_endpoint_channel.h",
"client_proxy.cc",
"client_proxy.h",
"encryption_runner.cc",
"encryption_runner.h",
"endpoint_channel.h",
"endpoint_channel_manager.cc",
"endpoint_channel_manager.h",
"endpoint_manager.cc",
"endpoint_manager.h",
@@ -72,6 +73,7 @@ cc_library(
"service_controller.h",
"service_controller_router.cc",
"service_controller_router.h",
"wifi_lan_endpoint_channel.h",
"wifi_lan_service_info.h",
"wifi_lan_upgrade_handler.cc",
"wifi_lan_upgrade_handler.h",
@@ -87,7 +89,6 @@ cc_library(
"//platform:types",
"//platform:utils",
"//platform/api",
"//platform/port:down_cast",
"//platform/port:string",
"//proto:connections_enums_portable_proto",
"//net/proto2/compat/public:proto2_lite",
@@ -96,13 +97,29 @@ cc_library(
],
)
# TODO(apolyudov): remove when api v2 rework is done.
cc_library(
name = "message_lite",
hdrs = [
"message_lite.h",
],
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/impl/default",
"//platform/api",
"//platform/impl/g3",
"//proto:connections_enums_portable_proto",
"//testing/base/public:gunit_main",
],
@@ -114,6 +131,8 @@ cc_test(
deps = [
":internal",
"//platform:utils",
"//platform/api",
"//platform/impl/g3",
"//platform/port:string",
"//testing/base/public:gunit_main",
],
@@ -124,6 +143,8 @@ cc_test(
srcs = ["ble_advertisement_test.cc"],
deps = [
":internal",
"//platform/api",
"//platform/impl/g3",
"//platform/port:string",
"//testing/base/public:gunit_main",
],
@@ -135,6 +156,8 @@ cc_test(
deps = [
":internal",
"//platform:utils",
"//platform/api",
"//platform/impl/g3",
"//platform/port:string",
"//testing/base/public:gunit_main",
],
@@ -149,6 +172,8 @@ cc_test(
":internal",
"//proto/connections:offline_wire_formats_portable_proto",
"//platform:types",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
+11 -3
View File
@@ -16,14 +16,21 @@ 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
@@ -53,6 +60,7 @@ target_sources(core_internal
pcp_manager.h
service_controller.h
service_controller_router.h
wifi_lan_endpoint_channel.h
wifi_lan_upgrade_handler.h
)
@@ -90,9 +98,9 @@ target_link_libraries(core_internal_test
gmock
gtest
gtest_main
platform_impl_default
platform_impl_default_cond_var
platform_impl_default_lock
platform_impl_g3
platform_impl_shared_posix_condition_variable
platform_impl_shared_posix_lock
platform_port_string
platform_utils
)
@@ -18,6 +18,7 @@
#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"
@@ -27,9 +28,10 @@ namespace connections {
// Defines the set of methods that need to be implemented to handle the
// per-Medium-specific operations needed to upgrade an EndpointChannel.
template <typename Platform>
class BandwidthUpgradeHandler {
public:
using Platform = platform::ImplementationPlatform;
virtual ~BandwidthUpgradeHandler() {}
// Reverts any changes made to the device in the process of upgrading
+7 -13
View File
@@ -20,38 +20,32 @@ namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
BandwidthUpgradeManager<Platform>::BandwidthUpgradeManager(
BandwidthUpgradeManager::BandwidthUpgradeManager(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<EndpointManager<Platform> > endpoint_manager)
: endpoint_manager_(endpoint_manager),
bandwidth_upgrade_handlers_(),
current_bandwidth_upgrade_handler_() {}
template <typename Platform>
BandwidthUpgradeManager<Platform>::~BandwidthUpgradeManager() {
BandwidthUpgradeManager::~BandwidthUpgradeManager() {
// TODO(ahlee): Make sure we don't repeat the mistake fixed in cl/201883908.
}
template <typename Platform>
void BandwidthUpgradeManager<Platform>::initiateBandwidthUpgradeForEndpoint(
void BandwidthUpgradeManager::initiateBandwidthUpgradeForEndpoint(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
proto::connections::Medium medium) {}
template <typename Platform>
void BandwidthUpgradeManager<Platform>::processIncomingOfflineFrame(
void BandwidthUpgradeManager::processIncomingOfflineFrame(
ConstPtr<OfflineFrame> offline_frame, const string& from_endpoint_id,
Ptr<ClientProxy<Platform> > to_client_proxy,
proto::connections::Medium current_medium) {}
template <typename Platform>
void BandwidthUpgradeManager<Platform>::processEndpointDisconnection(
void BandwidthUpgradeManager::processEndpointDisconnection(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
Ptr<CountDownLatch> process_disconnection_barrier) {}
template <typename Platform>
bool BandwidthUpgradeManager<Platform>::setCurrentBandwidthUpgradeHandler(
bool BandwidthUpgradeManager::setCurrentBandwidthUpgradeHandler(
proto::connections::Medium medium) {
return false;
}
+10 -11
View File
@@ -23,6 +23,7 @@
#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"
@@ -33,14 +34,15 @@ namespace connections {
// Manages all known {@link BandwidthUpgradeHandler} implementations, delegating
// operations to the appropriate one as per the parameters passed in.
template <typename Platform>
class BandwidthUpgradeManager
: public EndpointManager<Platform>::IncomingOfflineFrameProcessor {
: public EndpointManager<
platform::ImplementationPlatform>::IncomingOfflineFrameProcessor {
public:
BandwidthUpgradeManager(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<EndpointManager<Platform> > endpoint_manager);
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
@@ -64,17 +66,14 @@ class BandwidthUpgradeManager
bool setCurrentBandwidthUpgradeHandler(proto::connections::Medium medium);
Ptr<EndpointManager<Platform> > endpoint_manager_;
typedef std::map<proto::connections::Medium,
Ptr<BandwidthUpgradeHandler<Platform> > >
typedef std::map<proto::connections::Medium, Ptr<BandwidthUpgradeHandler>>
BandwidthUpgradeHandlersMap;
BandwidthUpgradeHandlersMap bandwidth_upgrade_handlers_;
Ptr<BandwidthUpgradeHandler<Platform> > current_bandwidth_upgrade_handler_;
Ptr<BandwidthUpgradeHandler> current_bandwidth_upgrade_handler_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#include "core/internal/bandwidth_upgrade_manager.cc"
#endif // CORE_INTERNAL_BANDWIDTH_UPGRADE_MANAGER_H_
@@ -18,138 +18,115 @@ namespace location {
namespace nearby {
namespace connections {
namespace {
using Platform = platform::ImplementationPlatform;
}
namespace base_bandwidth_upgrade_handler {
template <typename Platform>
class RevertRunnable : public Runnable {
public:
void run() {}
void run() override {}
};
template <typename Platform>
class InitiateBandwidthUpgradeForEndpointRunnable : public Runnable {
public:
void run() {}
void run() override {}
};
template <typename Platform>
class ProcessEndpointDisconnectionRunnable : public Runnable {
public:
void run() {}
void run() override {}
};
template <typename Platform>
class ProcessBandwidthUpgradeNegotiationFrameRunnable : public Runnable {
public:
void run() {}
void run() override {}
};
} // namespace base_bandwidth_upgrade_handler
template <typename Platform>
BaseBandwidthUpgradeHandler<Platform>::BaseBandwidthUpgradeHandler(
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager)
BaseBandwidthUpgradeHandler::BaseBandwidthUpgradeHandler(
Ptr<EndpointChannelManager> endpoint_channel_manager)
: endpoint_channel_manager_(endpoint_channel_manager),
alarm_executor_(),
serial_executor_(),
alarm_executor_(nullptr),
serial_executor_(nullptr),
previous_endpoint_channels_(),
in_progress_upgrades_(),
safe_to_close_write_timestamps_() {}
template <typename Platform>
BaseBandwidthUpgradeHandler<Platform>::~BaseBandwidthUpgradeHandler() {}
BaseBandwidthUpgradeHandler::~BaseBandwidthUpgradeHandler() {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::revert() {}
void BaseBandwidthUpgradeHandler::revert() {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::processEndpointDisconnection(
void BaseBandwidthUpgradeHandler::processEndpointDisconnection(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
Ptr<CountDownLatch> process_disconnection_barrier) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::initiateBandwidthUpgradeForEndpoint(
void BaseBandwidthUpgradeHandler::initiateBandwidthUpgradeForEndpoint(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::
processBandwidthUpgradeNegotiationFrame(
ConstPtr<BandwidthUpgradeNegotiationFrame>
bandwidth_upgrade_negotiation,
Ptr<ClientProxy<Platform> > to_client_proxy,
const string& from_endpoint_id,
proto::connections::Medium current_medium) {}
void BaseBandwidthUpgradeHandler::processBandwidthUpgradeNegotiationFrame(
ConstPtr<BandwidthUpgradeNegotiationFrame> bandwidth_upgrade_negotiation,
Ptr<ClientProxy<Platform> > to_client_proxy, const string& from_endpoint_id,
proto::connections::Medium current_medium) {}
template <typename Platform>
Ptr<EndpointChannelManager<Platform> >
BaseBandwidthUpgradeHandler<Platform>::getEndpointChannelManager() {
Ptr<EndpointChannelManager>
BaseBandwidthUpgradeHandler::getEndpointChannelManager() {
return endpoint_channel_manager_;
}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::onIncomingConnection(
void BaseBandwidthUpgradeHandler::onIncomingConnection(
Ptr<IncomingSocketConnection> incoming_socket_connection) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::runOnBandwidthUpgradeHandlerThread(
void BaseBandwidthUpgradeHandler::runOnBandwidthUpgradeHandlerThread(
Ptr<Runnable> runnable) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::runUpgradeProtocol(
void BaseBandwidthUpgradeHandler::runUpgradeProtocol(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
Ptr<EndpointChannel> new_endpoint_channel) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::
processBandwidthUpgradePathAvailableEvent(
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
upgrade_path_info,
proto::connections::Medium current_medium) {}
void BaseBandwidthUpgradeHandler::processBandwidthUpgradePathAvailableEvent(
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
upgrade_path_info,
proto::connections::Medium current_medium) {}
template <typename Platform>
Ptr<EndpointChannel> BaseBandwidthUpgradeHandler<Platform>::
processBandwidthUpgradePathAvailableEventInternal(
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
upgrade_path_info) {
Ptr<EndpointChannel>
BaseBandwidthUpgradeHandler::processBandwidthUpgradePathAvailableEventInternal(
const string& endpoint_id, Ptr<ClientProxy<Platform> > client_proxy,
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
upgrade_path_info) {
return Ptr<EndpointChannel>();
}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::processLastWriteToPriorChannelEvent(
void BaseBandwidthUpgradeHandler::processLastWriteToPriorChannelEvent(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::processSafeToClosePriorChannelEvent(
void BaseBandwidthUpgradeHandler::processSafeToClosePriorChannelEvent(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id) {}
template <typename Platform>
std::int64_t BaseBandwidthUpgradeHandler<Platform>::calculateCloseDelay(
std::int64_t BaseBandwidthUpgradeHandler::calculateCloseDelay(
const string& endpoint_id) {
return 0;
}
template <typename Platform>
std::int64_t
BaseBandwidthUpgradeHandler<Platform>::getMillisSinceSafeCloseWritten(
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.
template <typename Platform>
void BaseBandwidthUpgradeHandler<Platform>::
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).
template <typename Platform>
Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>
BaseBandwidthUpgradeHandler<Platform>::readClientIntroductionFrame(
BaseBandwidthUpgradeHandler::readClientIntroductionFrame(
Ptr<EndpointChannel> endpoint_channel) {
return Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>();
}
@@ -33,13 +33,9 @@ namespace connections {
namespace base_bandwidth_upgrade_handler {
template <typename>
class RevertRunnable;
template <typename>
class InitiateBandwidthUpgradeForEndpointRunnable;
template <typename>
class ProcessEndpointDisconnectionRunnable;
template <typename>
class ProcessBandwidthUpgradeNegotiationFrameRunnable;
} // namespace base_bandwidth_upgrade_handler
@@ -69,26 +65,28 @@ class ProcessBandwidthUpgradeNegotiationFrameRunnable;
// BANDWIDTH_UPGRADE_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL from the
// other, and upon doing so, close the prior EndpointChannel.
// </ul>
template <typename Platform>
class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler<Platform> {
class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler {
public:
BaseBandwidthUpgradeHandler(
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager);
~BaseBandwidthUpgradeHandler();
using Platform = platform::ImplementationPlatform;
void revert();
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);
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);
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);
proto::connections::Medium current_medium) override;
protected:
// Represents the incoming Socket the Initiator has gotten after initializing
@@ -131,7 +129,7 @@ class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler<Platform> {
// @BandwidthUpgradeHandlerThread
virtual proto::connections::Medium getUpgradeMedium() = 0;
Ptr<EndpointChannelManager<Platform> > getEndpointChannelManager();
Ptr<EndpointChannelManager> getEndpointChannelManager();
// Common functionality to take an incoming connection and go through the
// upgrade process.
// @BandwidthUpgradeHandlerThread
@@ -140,15 +138,11 @@ class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler<Platform> {
void runOnBandwidthUpgradeHandlerThread(Ptr<Runnable> runnable);
private:
template <typename>
friend class base_bandwidth_upgrade_handler::RevertRunnable;
template <typename>
friend class base_bandwidth_upgrade_handler::
InitiateBandwidthUpgradeForEndpointRunnable;
template <typename>
friend class base_bandwidth_upgrade_handler::
ProcessEndpointDisconnectionRunnable;
template <typename>
friend class base_bandwidth_upgrade_handler::
ProcessBandwidthUpgradeNegotiationFrameRunnable;
@@ -176,7 +170,7 @@ class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler<Platform> {
Ptr<BandwidthUpgradeNegotiationFrame::ClientIntroduction>
readClientIntroductionFrame(Ptr<EndpointChannel> endpoint_channel);
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager_;
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
@@ -198,6 +192,4 @@ class BaseBandwidthUpgradeHandler : public BandwidthUpgradeHandler<Platform> {
} // namespace nearby
} // namespace location
#include "core/internal/base_bandwidth_upgrade_handler.cc"
#endif // CORE_INTERNAL_BASE_BANDWIDTH_UPGRADE_HANDLER_H_
+37 -57
View File
@@ -16,6 +16,7 @@
#include <cassert>
#include "platform/api/platform.h"
#include "platform/synchronized.h"
#include "proto/connections_enums.pb.h"
@@ -25,6 +26,8 @@ namespace connections {
namespace {
using Platform = platform::ImplementationPlatform;
std::int32_t bytesToInt(ConstPtr<ByteArray> bytes) {
const char* int_bytes = bytes->getData();
@@ -47,36 +50,36 @@ ConstPtr<ByteArray> intToBytes(std::int32_t value) {
return MakeConstPtr(new ByteArray(int_bytes, sizeof(int_bytes)));
}
ExceptionOr<ConstPtr<ByteArray> > readExactly(Ptr<InputStream> reader,
std::int64_t size) {
ExceptionOr<ConstPtr<ByteArray>> readExactly(Ptr<InputStream> reader,
std::int64_t size) {
string buffer;
std::int64_t remaining_size = size;
while (remaining_size > 0) {
ExceptionOr<ConstPtr<ByteArray> > read_bytes = reader->read(remaining_size);
ExceptionOr<ConstPtr<ByteArray>> read_bytes = reader->read(remaining_size);
if (!read_bytes.ok()) {
if (Exception::IO == read_bytes.exception()) {
return ExceptionOr<ConstPtr<ByteArray> >(read_bytes.exception());
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes.exception());
}
}
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray> > scoped_read_bytes(read_bytes.result());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_bytes(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);
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
buffer.append(scoped_read_bytes->getData(), scoped_read_bytes->size());
remaining_size -= scoped_read_bytes->size();
}
return ExceptionOr<ConstPtr<ByteArray> >(
return ExceptionOr<ConstPtr<ByteArray>>(
MakeConstPtr(new ByteArray(buffer.data(), buffer.size())));
}
ExceptionOr<std::int32_t> readInt(Ptr<InputStream> reader) {
ExceptionOr<ConstPtr<ByteArray> > read_bytes =
ExceptionOr<ConstPtr<ByteArray>> read_bytes =
readExactly(reader, sizeof(std::int32_t));
if (!read_bytes.ok()) {
if (Exception::IO == read_bytes.exception()) {
@@ -84,7 +87,7 @@ ExceptionOr<std::int32_t> readInt(Ptr<InputStream> reader) {
}
}
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray> > scoped_read_bytes(read_bytes.result());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_bytes(read_bytes.result());
return ExceptionOr<std::int32_t>(bytesToInt(scoped_read_bytes.get()));
}
@@ -96,10 +99,9 @@ Exception::Value writeInt(Ptr<OutputStream> writer, std::int32_t value) {
} // namespace
// TODO(b/150763574): Move implementatiopn to header or .inc file.
template <typename Platform>
BaseEndpointChannel<Platform>::BaseEndpointChannel(const string& channel_name,
Ptr<InputStream> reader,
Ptr<OutputStream> writer)
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()),
@@ -114,8 +116,7 @@ BaseEndpointChannel<Platform>::BaseEndpointChannel(const string& channel_name,
Platform::createConditionVariable(is_paused_lock_.get())),
is_paused_(Platform::createAtomicBoolean(false)) {}
template <typename Platform>
BaseEndpointChannel<Platform>::~BaseEndpointChannel() {
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
@@ -128,14 +129,13 @@ BaseEndpointChannel<Platform>::~BaseEndpointChannel() {
// of this class).
}
template <typename Platform>
ExceptionOr<ConstPtr<ByteArray> > BaseEndpointChannel<Platform>::read() {
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());
return ExceptionOr<ConstPtr<ByteArray>>(read_int.exception());
}
}
@@ -145,11 +145,11 @@ ExceptionOr<ConstPtr<ByteArray> > BaseEndpointChannel<Platform>::read() {
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
ExceptionOr<ConstPtr<ByteArray> > read_bytes =
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());
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes.exception());
}
}
@@ -168,7 +168,7 @@ ExceptionOr<ConstPtr<ByteArray> > BaseEndpointChannel<Platform>::read() {
// short-circuit out of here on error.
read_bytes_result.destroy();
if (decoded_bytes == nullptr) {
return ExceptionOr<ConstPtr<ByteArray> >(
return ExceptionOr<ConstPtr<ByteArray>>(
Exception::INVALID_PROTOCOL_BUFFER);
}
read_bytes_result = MakeConstPtr(
@@ -176,16 +176,14 @@ ExceptionOr<ConstPtr<ByteArray> > BaseEndpointChannel<Platform>::read() {
}
last_read_timestamp_ = system_clock_->elapsedRealtime();
return ExceptionOr<ConstPtr<ByteArray> >(read_bytes_result);
return ExceptionOr<ConstPtr<ByteArray>>(read_bytes_result);
}
template <typename Platform>
Exception::Value BaseEndpointChannel<Platform>::write(
ConstPtr<ByteArray> data) {
Exception::Value BaseEndpointChannel::write(ConstPtr<ByteArray> data) {
Synchronized s(writer_lock_.get());
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray> > scoped_data(data);
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
if (isPaused()) {
blockUntilUnpaused();
@@ -205,7 +203,7 @@ Exception::Value BaseEndpointChannel<Platform>::write(
data_to_write = scoped_data.release();
}
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray> > scoped_data_to_write(data_to_write);
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()));
@@ -232,8 +230,7 @@ Exception::Value BaseEndpointChannel<Platform>::write(
return Exception::NONE;
}
template <typename Platform>
void BaseEndpointChannel<Platform>::close() {
void BaseEndpointChannel::close() {
// WARNING WARNING WARNING
//
// This block deviates from the corresponding Java code.
@@ -260,8 +257,7 @@ void BaseEndpointChannel<Platform>::close() {
// TODO(tracyzhou): Add logging.
}
template <typename Platform>
void BaseEndpointChannel<Platform>::close(
void BaseEndpointChannel::close(
proto::connections::DisconnectionReason reason) {
// WARNING WARNING WARNING
//
@@ -273,8 +269,7 @@ void BaseEndpointChannel<Platform>::close(
// TODO(tracyzhou): Add logging.
}
template <typename Platform>
string BaseEndpointChannel<Platform>::getType() {
string BaseEndpointChannel::getType() {
string subtype = isEncryptionEnabled() ? "ENCRYPTED_" : "";
switch (getMedium()) {
case proto::connections::Medium::BLUETOOTH:
@@ -292,46 +287,32 @@ string BaseEndpointChannel<Platform>::getType() {
}
}
template <typename Platform>
string BaseEndpointChannel<Platform>::getName() {
return channel_name_;
}
string BaseEndpointChannel::getName() { return channel_name_; }
template <typename Platform>
void BaseEndpointChannel<Platform>::enableEncryption(
void BaseEndpointChannel::enableEncryption(
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
assert(!encryption_context.isNull());
encryption_context_->set(encryption_context);
}
template <typename Platform>
bool BaseEndpointChannel<Platform>::isPaused() {
return is_paused_->get();
}
bool BaseEndpointChannel::isPaused() { return is_paused_->get(); }
template <typename Platform>
void BaseEndpointChannel<Platform>::pause() {
is_paused_->set(true);
}
void BaseEndpointChannel::pause() { is_paused_->set(true); }
template <typename Platform>
void BaseEndpointChannel<Platform>::resume() {
void BaseEndpointChannel::resume() {
is_paused_->set(false);
unblockPausedWriter();
}
template <typename Platform>
std::int64_t BaseEndpointChannel<Platform>::getLastReadTimestamp() {
std::int64_t BaseEndpointChannel::getLastReadTimestamp() {
return last_read_timestamp_;
}
template <typename Platform>
bool BaseEndpointChannel<Platform>::isEncryptionEnabled() {
bool BaseEndpointChannel::isEncryptionEnabled() {
return !encryption_context_->get().isNull();
}
template <typename Platform>
void BaseEndpointChannel<Platform>::unblockPausedWriter() {
void BaseEndpointChannel::unblockPausedWriter() {
Synchronized s(is_paused_lock_.get());
// Notify to tell the thread calling wait() to check again.
@@ -343,8 +324,7 @@ void BaseEndpointChannel<Platform>::unblockPausedWriter() {
is_paused_condition_variable_->notify();
}
template <typename Platform>
void BaseEndpointChannel<Platform>::blockUntilUnpaused() {
void BaseEndpointChannel::blockUntilUnpaused() {
Synchronized s(is_paused_lock_.get());
// For more on how this works, see
+3 -5
View File
@@ -30,15 +30,15 @@
#include "platform/ptr.h"
#include "proto/connections_enums.pb.h"
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
class BaseEndpointChannel : public EndpointChannel {
public:
BaseEndpointChannel(const string& channel_name, Ptr<InputStream> reader,
BaseEndpointChannel(absl::string_view channel_name, Ptr<InputStream> reader,
Ptr<OutputStream> writer);
~BaseEndpointChannel() override;
@@ -83,7 +83,7 @@ class BaseEndpointChannel : public EndpointChannel {
private:
// Used to sanity check that our frame sizes are reasonable.
static const std::int32_t kMaxAllowedReadBytes = 1048576; // 1MB
static constexpr std::int32_t kMaxAllowedReadBytes = 1048576; // 1MB
bool isEncryptionEnabled();
void unblockPausedWriter();
@@ -121,6 +121,4 @@ class BaseEndpointChannel : public EndpointChannel {
} // namespace nearby
} // namespace location
#include "core/internal/base_endpoint_channel.cc"
#endif // CORE_INTERNAL_BASE_ENDPOINT_CHANNEL_H_
@@ -14,7 +14,7 @@
#include "core/internal/base_endpoint_channel.h"
#include "platform/impl/default/default_platform.h"
#include "platform/api/platform.h"
#include "platform/pipe.h"
#include "proto/connections_enums.pb.h"
#include "gmock/gmock.h"
@@ -25,21 +25,7 @@ namespace nearby {
namespace connections {
namespace {
class TestPlatform : public DefaultPlatform {
public:
static SystemClock* createSystemClock() { return nullptr; }
static Ptr<AtomicBoolean> createAtomicBoolean(bool initial_value) {
return Ptr<AtomicBoolean>();
}
template <typename T>
static Ptr<AtomicReference<T>> createAtomicReference(const T& initial_value) {
return Ptr<AtomicReference<T>>();
}
};
class TestEndpointChannel : public BaseEndpointChannel<TestPlatform> {
class TestEndpointChannel : public BaseEndpointChannel {
public:
explicit TestEndpointChannel(Ptr<InputStream> input_stream)
: BaseEndpointChannel("channel", input_stream, Ptr<OutputStream>()) {}
@@ -48,7 +34,7 @@ class TestEndpointChannel : public BaseEndpointChannel<TestPlatform> {
MOCK_METHOD(void, closeImpl, (), (override));
};
using SamplePipe = Pipe<TestPlatform>;
using SamplePipe = Pipe;
TEST(BaseEndpointChannelTest, ReadAfterInputStreamClosed) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
+14 -11
View File
@@ -62,12 +62,7 @@ class StartAdvertisingCallable : public Callable<Status::Value> {
service_id_(service_id),
local_endpoint_name_(local_endpoint_name),
options_(options),
// Convert the passed in connection_lifecycle_listener Ptr into a
// reference counted one. The advertising session and any connected
// endpoints need a handle to the same connection_lifecycle_listener, so
// there is no clear model of who actually owns the listener.
connection_lifecycle_listener_(
MakeRefCountedPtr(&(*connection_lifecycle_listener))) {}
connection_lifecycle_listener_(connection_lifecycle_listener) {}
ExceptionOr<Status::Value> call() override {
// Ask the implementation to attempt to start advertising.
@@ -689,8 +684,8 @@ const std::int64_t
template <typename Platform>
BasePCPHandler<Platform>::BasePCPHandler(
Ptr<EndpointManager<Platform>> endpoint_manager,
Ptr<EndpointChannelManager<Platform>> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform>> bandwidth_upgrade_manager)
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager)
: endpoint_manager_(endpoint_manager),
endpoint_channel_manager_(endpoint_channel_manager),
bandwidth_upgrade_manager_(bandwidth_upgrade_manager),
@@ -1151,6 +1146,14 @@ Exception::Value BasePCPHandler<Platform>::onIncomingConnection(
return Exception::IO;
}
// The ConnectionRequest frame has two fields that both contain the
// EndpointInfo. The legacy field stores it as a string while the newer field
// stores it as a byte array. We'll attempt to grab from the newer field, but
// will accept the older string if it's all that exists.
const std::string& endpoint_name = connection_request.has_endpoint_info()
? connection_request.endpoint_info()
: connection_request.endpoint_name();
// We've successfully connected to the device, and are now about to jump on to
// the EncryptionRunner thread to start running our encryption protocol. We'll
// mark ourselves as pending in case we get another call to requestConnection
@@ -1160,7 +1163,7 @@ Exception::Value BasePCPHandler<Platform>::onIncomingConnection(
.insert(std::make_pair(
connection_request.endpoint_id(),
PendingConnectionInfo::newIncomingPendingConnectionInfo(
client_proxy, connection_request.endpoint_name(),
client_proxy, endpoint_name,
scoped_endpoint_channel.release(), connection_request.nonce(),
start_time_millis, advertising_connection_lifecycle_listener_,
OfflineFrames::connectionRequestMediumsToMediums(
@@ -1392,7 +1395,7 @@ void BasePCPHandler<Platform>::evaluateConnectionResult(
} else {
pending_rejected_connection_close_alarms_.insert(std::make_pair(
endpoint_id,
MakePtr(new CancelableAlarm<Platform>(
MakePtr(new CancelableAlarm(
"BasePCPHandler.evaluateConnectionResult() delayed close",
MakePtr(
new base_pcp_handler::
@@ -1421,7 +1424,7 @@ BasePCPHandler<Platform>::readConnectionRequestFrame(
// To avoid a device connecting but never sending their introductory frame, we
// time out the connection after a certain amount of time.
CancelableAlarm<Platform> timeout_alarm(
CancelableAlarm timeout_alarm(
"PCPHandler(" + this->getStrategy().getName() +
").readConnectionRequestFrame",
MakePtr(
+6 -7
View File
@@ -83,10 +83,9 @@ class BasePCPHandler
public EndpointManager<Platform>::IncomingOfflineFrameProcessor {
public:
// TODO(tracyzhou): Add SecureRandom.
BasePCPHandler(
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager);
BasePCPHandler(Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
~BasePCPHandler() override;
// We have been asked by the client to start advertising. Once we successfully
@@ -253,8 +252,8 @@ class BasePCPHandler
virtual proto::connections::Medium getDefaultUpgradeMedium() = 0;
Ptr<EndpointManager<Platform> > endpoint_manager_;
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager_;
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager_;
Ptr<EndpointChannelManager> endpoint_channel_manager_;
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager_;
private:
template <typename>
@@ -487,7 +486,7 @@ class BasePCPHandler
// 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<Platform> > >
typedef std::map<string, Ptr<CancelableAlarm> >
PendingRejectedConnectionCloseAlarmsMap;
PendingRejectedConnectionCloseAlarmsMap
pending_rejected_connection_close_alarms_;
+11 -22
View File
@@ -20,42 +20,31 @@ namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
Ptr<BLEEndpointChannel<Platform> >
BLEEndpointChannel<Platform>::createOutgoing(
Ptr<BLEEndpointChannel> BLEEndpointChannel::createOutgoing(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BLESocket> ble_socket) {
return MakePtr(
new BLEEndpointChannel<Platform>(channel_name, ble_socket));
return MakePtr(new BLEEndpointChannel(channel_name, ble_socket));
}
template <typename Platform>
Ptr<BLEEndpointChannel<Platform> >
BLEEndpointChannel<Platform>::createIncoming(
Ptr<BLEEndpointChannel> BLEEndpointChannel::createIncoming(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BLESocket> ble_socket) {
return MakePtr(
new BLEEndpointChannel<Platform>(channel_name, ble_socket));
return MakePtr(new BLEEndpointChannel(channel_name, ble_socket));
}
template <typename Platform>
BLEEndpointChannel<Platform>::BLEEndpointChannel(
const string& channel_name, Ptr<BLESocket> ble_socket)
: BaseEndpointChannel<Platform>(channel_name,
ble_socket->getInputStream(),
ble_socket->getOutputStream()),
BLEEndpointChannel::BLEEndpointChannel(const string& channel_name,
Ptr<BLESocket> ble_socket)
: BaseEndpointChannel(channel_name, ble_socket->getInputStream(),
ble_socket->getOutputStream()),
ble_socket_(ble_socket) {}
template <typename Platform>
BLEEndpointChannel<Platform>::~BLEEndpointChannel() {}
BLEEndpointChannel::~BLEEndpointChannel() {}
template <typename Platform>
proto::connections::Medium BLEEndpointChannel<Platform>::getMedium() {
proto::connections::Medium BLEEndpointChannel::getMedium() {
return proto::connections::Medium::BLE;
}
template <typename Platform>
void BLEEndpointChannel<Platform>::closeImpl() {
void BLEEndpointChannel::closeImpl() {
Exception::Value exception = ble_socket_->close();
if (exception != Exception::NONE) {
if (exception == Exception::IO) {
+6 -6
View File
@@ -18,6 +18,7 @@
#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 "proto/connections_enums.pb.h"
@@ -26,13 +27,14 @@ namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
class BLEEndpointChannel : public BaseEndpointChannel<Platform> {
class BLEEndpointChannel : public BaseEndpointChannel {
public:
static Ptr<BLEEndpointChannel<Platform> > createOutgoing(
using Platform = platform::ImplementationPlatform;
static Ptr<BLEEndpointChannel> createOutgoing(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BLESocket> ble_socket);
static Ptr<BLEEndpointChannel<Platform> > createIncoming(
static Ptr<BLEEndpointChannel> createIncoming(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BLESocket> ble_socket);
@@ -53,6 +55,4 @@ class BLEEndpointChannel : public BaseEndpointChannel<Platform> {
} // namespace nearby
} // namespace location
#include "core/internal/ble_endpoint_channel.cc"
#endif // CORE_INTERNAL_BLE_ENDPOINT_CHANNEL_H_
+10 -21
View File
@@ -20,42 +20,31 @@ namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
Ptr<BluetoothEndpointChannel<Platform> >
BluetoothEndpointChannel<Platform>::createOutgoing(
Ptr<BluetoothEndpointChannel> BluetoothEndpointChannel::createOutgoing(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BluetoothSocket> bluetooth_socket) {
return MakePtr(
new BluetoothEndpointChannel<Platform>(channel_name, bluetooth_socket));
return MakePtr(new BluetoothEndpointChannel(channel_name, bluetooth_socket));
}
template <typename Platform>
Ptr<BluetoothEndpointChannel<Platform> >
BluetoothEndpointChannel<Platform>::createIncoming(
Ptr<BluetoothEndpointChannel> BluetoothEndpointChannel::createIncoming(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BluetoothSocket> bluetooth_socket) {
return MakePtr(
new BluetoothEndpointChannel<Platform>(channel_name, bluetooth_socket));
return MakePtr(new BluetoothEndpointChannel(channel_name, bluetooth_socket));
}
template <typename Platform>
BluetoothEndpointChannel<Platform>::BluetoothEndpointChannel(
BluetoothEndpointChannel::BluetoothEndpointChannel(
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket)
: BaseEndpointChannel<Platform>(channel_name,
bluetooth_socket->getInputStream(),
bluetooth_socket->getOutputStream()),
: BaseEndpointChannel(channel_name, bluetooth_socket->getInputStream(),
bluetooth_socket->getOutputStream()),
bluetooth_socket_(bluetooth_socket) {}
template <typename Platform>
BluetoothEndpointChannel<Platform>::~BluetoothEndpointChannel() {}
BluetoothEndpointChannel::~BluetoothEndpointChannel() {}
template <typename Platform>
proto::connections::Medium BluetoothEndpointChannel<Platform>::getMedium() {
proto::connections::Medium BluetoothEndpointChannel::getMedium() {
return proto::connections::Medium::BLUETOOTH;
}
template <typename Platform>
void BluetoothEndpointChannel<Platform>::closeImpl() {
void BluetoothEndpointChannel::closeImpl() {
Exception::Value exception = bluetooth_socket_->close();
if (exception != Exception::NONE) {
if (exception == Exception::IO) {
@@ -18,6 +18,7 @@
#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 "proto/connections_enums.pb.h"
@@ -26,13 +27,14 @@ namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
class BluetoothEndpointChannel : public BaseEndpointChannel<Platform> {
class BluetoothEndpointChannel : public BaseEndpointChannel {
public:
static Ptr<BluetoothEndpointChannel<Platform> > createOutgoing(
using Platform = platform::ImplementationPlatform;
static Ptr<BluetoothEndpointChannel> createOutgoing(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BluetoothSocket> bluetooth_socket);
static Ptr<BluetoothEndpointChannel<Platform> > createIncoming(
static Ptr<BluetoothEndpointChannel> createIncoming(
Ptr<MediumManager<Platform> > medium_manager, const string& channel_name,
Ptr<BluetoothSocket> bluetooth_socket);
@@ -54,6 +56,4 @@ class BluetoothEndpointChannel : public BaseEndpointChannel<Platform> {
} // namespace nearby
} // namespace location
#include "core/internal/bluetooth_endpoint_channel.cc"
#endif // CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_
+22 -22
View File
@@ -115,7 +115,7 @@ class ServerRunnable : public Runnable {
encryption_result_listener_(encryption_result_listener) {}
void run() override {
CancelableAlarm<Platform> timeout_alarm(
CancelableAlarm timeout_alarm(
"EncryptionRunner.startServer() timeout",
MakePtr(new CancelableAlarmRunnable<Platform>(
client_proxy_, endpoint_id_, endpoint_channel_)),
@@ -126,7 +126,7 @@ class ServerRunnable : public Runnable {
// Java code throws a HandshakeException.
if (server == nullptr) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -135,7 +135,7 @@ class ServerRunnable : public Runnable {
if (!client_init.ok()) {
if (Exception::IO == client_init.exception()) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -152,7 +152,7 @@ class ServerRunnable : public Runnable {
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
}
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -165,7 +165,7 @@ class ServerRunnable : public Runnable {
// Java code throws a HandshakeException.
if (server_init == nullptr) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -174,7 +174,7 @@ class ServerRunnable : public Runnable {
if (Exception::NONE != write_exception) {
if (Exception::IO == write_exception) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -188,7 +188,7 @@ class ServerRunnable : public Runnable {
if (!client_finish.ok()) {
if (Exception::IO == client_finish.exception()) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -203,7 +203,7 @@ class ServerRunnable : public Runnable {
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
}
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -216,7 +216,7 @@ class ServerRunnable : public Runnable {
MakePtr(server.release()),
encryption_result_listener_.get())) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -227,8 +227,8 @@ class ServerRunnable : public Runnable {
endpoint_id_.c_str());
}
void handleHandshakeOrIOException(CancelableAlarm<Platform>& timeout_alarm) {
timeout_alarm.cancel();
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
timeout_alarm->cancel();
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
endpoint_channel_);
}
@@ -272,7 +272,7 @@ class ClientRunnable : public Runnable {
encryption_result_listener_(encryption_result_listener) {}
void run() override {
CancelableAlarm<Platform> timeout_alarm(
CancelableAlarm timeout_alarm(
"EncryptionRunner.startClient() timeout",
MakePtr(new CancelableAlarmRunnable<Platform>(
client_proxy_, endpoint_id_, endpoint_channel_)),
@@ -284,7 +284,7 @@ class ClientRunnable : public Runnable {
// Java code throws a HandshakeException.
if (client == nullptr) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -294,7 +294,7 @@ class ClientRunnable : public Runnable {
// Java code throws a HandshakeException.
if (client_init == nullptr) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -303,7 +303,7 @@ class ClientRunnable : public Runnable {
if (Exception::NONE != write_init_exception) {
if (Exception::IO == write_init_exception) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -317,7 +317,7 @@ class ClientRunnable : public Runnable {
if (!server_init.ok()) {
if (Exception::IO == server_init.exception()) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -333,7 +333,7 @@ class ClientRunnable : public Runnable {
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
}
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -346,7 +346,7 @@ class ClientRunnable : public Runnable {
// Java code throws a HandshakeException.
if (client_finish == nullptr) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
@@ -356,7 +356,7 @@ class ClientRunnable : public Runnable {
if (Exception::NONE != write_finish_exception) {
if (Exception::IO == write_finish_exception) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -370,7 +370,7 @@ class ClientRunnable : public Runnable {
MakePtr(client.release()),
encryption_result_listener_.get())) {
logException();
handleHandshakeOrIOException(timeout_alarm);
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
@@ -381,8 +381,8 @@ class ClientRunnable : public Runnable {
endpoint_id_.c_str());
}
void handleHandshakeOrIOException(CancelableAlarm<Platform>& timeout_alarm) {
timeout_alarm.cancel();
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
timeout_alarm->cancel();
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
endpoint_channel_);
}
+46 -54
View File
@@ -16,21 +16,20 @@
#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"
namespace location {
namespace nearby {
namespace connections {
template <typename Platform>
EndpointChannelManager<Platform>::EndpointChannelManager(
EndpointChannelManager::EndpointChannelManager(
Ptr<MediumManager<Platform> > medium_manager)
: lock_(Platform::createLock()),
medium_manager_(medium_manager),
channel_state_(new ChannelState()) {}
template <typename Platform>
EndpointChannelManager<Platform>::~EndpointChannelManager() {
EndpointChannelManager::~EndpointChannelManager() {
Synchronized s(lock_.get());
// TODO(tracyzhou): logger.atDebug().log("Initiating shutdown of
@@ -40,40 +39,47 @@ EndpointChannelManager<Platform>::~EndpointChannelManager() {
// down.");
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::createOutgoingBluetoothEndpointChannel(
EndpointChannelManager::createOutgoingBluetoothEndpointChannel(
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket) {
return BluetoothEndpointChannel<Platform>::createOutgoing(
medium_manager_, channel_name, bluetooth_socket);
return BluetoothEndpointChannel::createOutgoing(medium_manager_, channel_name,
bluetooth_socket);
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::createIncomingBluetoothEndpointChannel(
EndpointChannelManager::createIncomingBluetoothEndpointChannel(
const string& channel_name, Ptr<BluetoothSocket> bluetooth_socket) {
return BluetoothEndpointChannel<Platform>::createIncoming(
medium_manager_, channel_name, bluetooth_socket);
return BluetoothEndpointChannel::createIncoming(medium_manager_, channel_name,
bluetooth_socket);
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::createOutgoingBLEEndpointChannel(
Ptr<EndpointChannel> EndpointChannelManager::createOutgoingBLEEndpointChannel(
const string& channel_name, Ptr<BLESocket> ble_socket) {
return BLEEndpointChannel<Platform>::createOutgoing(medium_manager_,
channel_name, ble_socket);
return BLEEndpointChannel::createOutgoing(medium_manager_, channel_name,
ble_socket);
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::createIncomingBLEEndpointChannel(
Ptr<EndpointChannel> EndpointChannelManager::createIncomingBLEEndpointChannel(
const string& channel_name, Ptr<BLESocket> ble_socket) {
return BLEEndpointChannel<Platform>::createIncoming(medium_manager_,
channel_name, ble_socket);
return BLEEndpointChannel::createIncoming(medium_manager_, channel_name,
ble_socket);
}
template <typename Platform>
void EndpointChannelManager<Platform>::registerChannelForEndpoint(
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());
@@ -88,9 +94,7 @@ void EndpointChannelManager<Platform>::registerChannelForEndpoint(
}
#ifdef BANDWIDTH_UPGRADE_MANAGER_IMPLEMENTED
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::replaceChannelForEndpoint(
Ptr<EndpointChannel> EndpointChannelManager::replaceChannelForEndpoint(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel) {
Synchronized s(lock_.get());
@@ -110,8 +114,7 @@ EndpointChannelManager<Platform>::replaceChannelForEndpoint(
}
#endif
template <typename Platform>
bool EndpointChannelManager<Platform>::encryptChannelForEndpoint(
bool EndpointChannelManager::encryptChannelForEndpoint(
const string& endpoint_id,
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
Synchronized s(lock_.get());
@@ -139,16 +142,14 @@ bool EndpointChannelManager<Platform>::encryptChannelForEndpoint(
return true;
}
template <typename Platform>
Ptr<EndpointChannel> EndpointChannelManager<Platform>::getChannelForEndpoint(
Ptr<EndpointChannel> EndpointChannelManager::getChannelForEndpoint(
const string& endpoint_id) {
Synchronized s(lock_.get());
return channel_state_->getChannelForEndpoint(endpoint_id);
}
template <typename Platform>
void EndpointChannelManager<Platform>::setActiveEndpointChannel(
void EndpointChannelManager::setActiveEndpointChannel(
Ptr<ClientProxy<Platform> > client_proxy, const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel) {
#ifdef BANDWIDTH_UPGRADE_MANAGER_IMPLEMENTED
@@ -169,8 +170,7 @@ void EndpointChannelManager<Platform>::setActiveEndpointChannel(
channel_state_->updateChannelForEndpoint(endpoint_id, endpoint_channel));
}
template <typename Platform>
void EndpointChannelManager<Platform>::encryptChannel(
void EndpointChannelManager::encryptChannel(
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel,
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
// TODO(tracyzhou): Add logging.
@@ -179,8 +179,7 @@ void EndpointChannelManager<Platform>::encryptChannel(
///////////////////////////////// ChannelState /////////////////////////////////
template <typename Platform>
EndpointChannelManager<Platform>::ChannelState::~ChannelState() {
EndpointChannelManager::ChannelState::~ChannelState() {
while (!endpoint_id_to_metadata_.empty()) {
typename EndpointIdToMetadataMap::iterator it =
endpoint_id_to_metadata_.begin();
@@ -190,15 +189,13 @@ EndpointChannelManager<Platform>::ChannelState::~ChannelState() {
}
}
template <typename Platform>
bool EndpointChannelManager<Platform>::ChannelState::isEndpointEncrypted(
bool EndpointChannelManager::ChannelState::isEndpointEncrypted(
const string& endpoint_id) {
return !getEncryptionContextForEndpoint(endpoint_id).isNull();
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::ChannelState::updateChannelForEndpoint(
EndpointChannelManager::ChannelState::updateChannelForEndpoint(
const string& endpoint_id, Ptr<EndpointChannel> endpoint_channel) {
Ptr<EndpointChannel> previous_endpoint_channel;
Ptr<EndpointMetaData> endpoint_metadata;
@@ -222,11 +219,10 @@ EndpointChannelManager<Platform>::ChannelState::updateChannelForEndpoint(
return scoped_previous_endpoint_channel.release();
}
template <typename Platform>
Ptr<securegcm::D2DConnectionContextV1> EndpointChannelManager<Platform>::
ChannelState::updateEncryptionContextForEndpoint(
const string& endpoint_id,
Ptr<securegcm::D2DConnectionContextV1> encryption_context) {
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;
@@ -248,8 +244,7 @@ Ptr<securegcm::D2DConnectionContextV1> EndpointChannelManager<Platform>::
return scoped_previous_encryption_context.release();
}
template <typename Platform>
bool EndpointChannelManager<Platform>::ChannelState::removeEndpoint(
bool EndpointChannelManager::ChannelState::removeEndpoint(
const string& endpoint_id, proto::connections::DisconnectionReason reason) {
typename EndpointIdToMetadataMap::iterator it =
endpoint_id_to_metadata_.find(endpoint_id);
@@ -263,9 +258,8 @@ bool EndpointChannelManager<Platform>::ChannelState::removeEndpoint(
return true;
}
template <typename Platform>
Ptr<securegcm::D2DConnectionContextV1>
EndpointChannelManager<Platform>::ChannelState::getEncryptionContextForEndpoint(
EndpointChannelManager::ChannelState::getEncryptionContextForEndpoint(
const string& endpoint_id) {
typename EndpointIdToMetadataMap::iterator it =
endpoint_id_to_metadata_.find(endpoint_id);
@@ -276,9 +270,8 @@ EndpointChannelManager<Platform>::ChannelState::getEncryptionContextForEndpoint(
return it->second->encryption_context;
}
template <typename Platform>
Ptr<EndpointChannel>
EndpointChannelManager<Platform>::ChannelState::getChannelForEndpoint(
EndpointChannelManager::ChannelState::getChannelForEndpoint(
const string& endpoint_id) {
typename EndpointIdToMetadataMap::iterator it =
endpoint_id_to_metadata_.find(endpoint_id);
@@ -289,8 +282,7 @@ EndpointChannelManager<Platform>::ChannelState::getChannelForEndpoint(
return it->second->endpoint_channel;
}
template <typename Platform>
bool EndpointChannelManager<Platform>::unregisterChannelForEndpoint(
bool EndpointChannelManager::unregisterChannelForEndpoint(
const string& endpoint_id) {
Synchronized s(lock_.get());
+10 -4
View File
@@ -23,6 +23,8 @@
#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 "securegcm/d2d_connection_context_v1.h"
@@ -36,10 +38,11 @@ namespace connections {
//
// The factory methods would be static, but for the fact that they need to use
// the MediumManager.
template <typename Platform>
class EndpointChannelManager {
public:
explicit EndpointChannelManager(Ptr<MediumManager<Platform> > medium_manager);
using Platform = platform::ImplementationPlatform;
explicit EndpointChannelManager(Ptr<MediumManager<Platform>> medium_manager);
~EndpointChannelManager();
Ptr<EndpointChannel> createOutgoingBluetoothEndpointChannel(
@@ -52,6 +55,11 @@ class EndpointChannelManager {
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.
@@ -152,6 +160,4 @@ class EndpointChannelManager {
} // namespace nearby
} // namespace location
#include "core/internal/endpoint_channel_manager.cc"
#endif // CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
+1 -1
View File
@@ -480,7 +480,7 @@ const std::int32_t EndpointManager<Platform>::kMaxConcurrentEndpoints = 50;
template <typename Platform>
EndpointManager<Platform>::EndpointManager(
Ptr<EndpointChannelManager<Platform>> endpoint_channel_manager)
Ptr<EndpointChannelManager> endpoint_channel_manager)
: thread_utils_(Platform::createThreadUtils()),
system_clock_(Platform::createSystemClock()),
endpoint_channel_manager_(endpoint_channel_manager),
+2 -2
View File
@@ -109,7 +109,7 @@ class EndpointManager {
};
explicit EndpointManager(
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager);
Ptr<EndpointChannelManager> endpoint_channel_manager);
~EndpointManager();
// Invoked from the constructors of the various *Manager components that make
@@ -225,7 +225,7 @@ class EndpointManager {
ScopedPtr<Ptr<ThreadUtils> > thread_utils_;
ScopedPtr<Ptr<SystemClock> > system_clock_;
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager_;
Ptr<EndpointChannelManager> endpoint_channel_manager_;
typedef std::map<V1Frame::FrameType, Ptr<IncomingOfflineFrameProcessor> >
IncomingOfflineFrameProcessorsMap;
@@ -122,7 +122,7 @@ class OutgoingStreamInternalPayload : public InternalPayload {
}
private:
static const std::int64_t kChunkSize = 64 * 1024;
static constexpr std::int64_t kChunkSize = 64 * 1024;
};
template <typename Platform>
@@ -205,7 +205,7 @@ class OutgoingFileInternalPayload : public InternalPayload {
void close() override { payload_->asFile()->asInputFile()->close(); }
private:
static const std::int64_t kChunkSize = 64 * 1024;
static constexpr std::int64_t kChunkSize = 64 * 1024;
};
class IncomingFileInternalPayload : public InternalPayload {
@@ -291,14 +291,13 @@ Ptr<InternalPayload> InternalPayloadFactory<Platform>::createIncoming(
case PayloadTransferFrame::PayloadHeader::STREAM: {
// pipe will be auto-destroyed when it is no longer referenced.
auto pipe = MakeRefCountedPtr(new Pipe<Platform>());
auto pipe = MakeRefCountedPtr(new Pipe());
return MakePtr(new IncomingStreamInternalPayload<Platform>(
MakeConstPtr(new Payload(
payload_id,
MakeConstPtr(new Payload::Stream(
Pipe<Platform>::createInputStream(pipe))))),
Pipe<Platform>::createOutputStream(pipe)));
MakeConstPtr(
new Payload(payload_id, MakeConstPtr(new Payload::Stream(
Pipe::createInputStream(pipe))))),
Pipe::createOutputStream(pipe)));
}
case PayloadTransferFrame::PayloadHeader::FILE: {
+128 -1
View File
@@ -24,13 +24,15 @@ template <typename Platform>
MediumManager<Platform>::MediumManager()
: mediums_(new Mediums<Platform>()),
bluetooth_classic_lock_(Platform::createLock()),
ble_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.");
@@ -370,6 +372,131 @@ Ptr<BLESocket> MediumManager<Platform>::connectToBlePeripheral(
#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
+40
View File
@@ -136,6 +136,45 @@ class MediumManager {
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.
@@ -143,6 +182,7 @@ class MediumManager {
ScopedPtr<Ptr<Lock> > bluetooth_classic_lock_;
ScopedPtr<Ptr<Lock> > ble_lock_;
ScopedPtr<Ptr<Lock> > wifi_lan_lock_;
};
} // namespace connections
+35 -4
View File
@@ -12,6 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
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 = [
@@ -19,8 +39,6 @@ cc_library(
"ble_advertisement_header.cc",
"ble_packet.cc",
"ble_peripheral.cc",
"utils.cc",
"utils.h",
],
hdrs = [
"advertisement_read_result.cc",
@@ -48,9 +66,12 @@ cc_library(
"mediums.h",
"uuid.cc",
"uuid.h",
"wifi_lan.cc",
"wifi_lan.h",
],
visibility = ["//core/internal:__pkg__"],
deps = [
":utils",
"//platform:logging",
"//platform:types",
"//platform:utils",
@@ -67,7 +88,8 @@ cc_test(
srcs = ["advertisement_read_result_test.cc"],
deps = [
":mediums",
"//platform/impl/default",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
"//absl/time",
],
@@ -79,6 +101,8 @@ cc_test(
deps = [
":mediums",
"//platform:utils",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
@@ -88,6 +112,8 @@ cc_test(
srcs = ["ble_advertisement_test.cc"],
deps = [
":mediums",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
@@ -97,6 +123,8 @@ cc_test(
srcs = ["ble_packet_test.cc"],
deps = [
":mediums",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
@@ -106,6 +134,8 @@ cc_test(
srcs = ["bloom_filter_test.cc"],
deps = [
":mediums",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
@@ -115,7 +145,8 @@ cc_test(
srcs = ["lost_entity_tracker_test.cc"],
deps = [
":mediums",
"//platform/impl/default",
"//platform/api",
"//platform/impl/g3",
"//testing/base/public:gunit_main",
],
)
+1 -1
View File
@@ -66,7 +66,7 @@ target_link_libraries(core_internal_mediums_test
core_internal_mediums
gtest
gtest_main
platform_impl_default
platform_impl_g3
platform_utils
)
@@ -14,7 +14,7 @@
#include "core/internal/mediums/advertisement_read_result.h"
#include "platform/impl/default/default_platform.h"
#include "platform/api/platform.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
@@ -24,23 +24,7 @@ namespace nearby {
namespace connections {
namespace mediums {
class SampleSystemClock : public SystemClock {
public:
SampleSystemClock() {}
~SampleSystemClock() override {}
std::int64_t elapsedRealtime() override {
return absl::ToUnixMillis(absl::Now());
}
};
class SamplePlatform {
public:
static Ptr<Lock> createLock() { return DefaultPlatform::createLock(); }
static Ptr<SystemClock> createSystemClock() {
return MakePtr(new SampleSystemClock());
}
};
using TestPlatform = platform::ImplementationPlatform;
constexpr char kAdvertisementBytes[] = {0x0A, 0x0B, 0x0C};
@@ -53,16 +37,16 @@ const absl::Duration kAdvertisementMaxBackoffDuration =
template <>
const std::int64_t AdvertisementReadResult<
SamplePlatform>::kAdvertisementMaxBackoffDurationMillis =
TestPlatform>::kAdvertisementMaxBackoffDurationMillis =
ToInt64Milliseconds(kAdvertisementMaxBackoffDuration);
template <>
const std::int64_t
AdvertisementReadResult<
SamplePlatform>::kAdvertisementBaseBackoffDurationMillis =
TestPlatform>::kAdvertisementBaseBackoffDurationMillis =
ToInt64Milliseconds(kAdvertisementBaseBackoffDuration);
TEST(AdvertisementReadResultTest, AdvertisementExists) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
@@ -75,7 +59,7 @@ TEST(AdvertisementReadResultTest, AdvertisementExists) {
}
TEST(AdvertisementReadResultTest, AdvertisementNonExistent) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
@@ -84,23 +68,23 @@ TEST(AdvertisementReadResultTest, AdvertisementNonExistent) {
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusInitialized) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
AdvertisementReadResult<SamplePlatform>::RetryStatus::RETRY);
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusSuccess) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
AdvertisementReadResult<
SamplePlatform>::RetryStatus::PREVIOUSLY_SUCCEEDED);
TestPlatform>::RetryStatus::PREVIOUSLY_SUCCEEDED);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusTooSoon) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
// Sleep for some time, but not long enough to warrant a retry.
@@ -108,22 +92,22 @@ TEST(AdvertisementReadResultTest, EvaluateRetryStatusTooSoon) {
absl::ToInt64Milliseconds(kAdvertisementBaseBackoffDuration) / 2));
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
AdvertisementReadResult<SamplePlatform>::RetryStatus::TOO_SOON);
AdvertisementReadResult<TestPlatform>::RetryStatus::TOO_SOON);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusRetry) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
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<SamplePlatform>::RetryStatus::RETRY);
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
}
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoff) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ false);
// Record an additional failure so our backoff duration increases.
@@ -134,11 +118,11 @@ TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoff) {
absl::SleepFor(kAdvertisementBaseBackoffDuration);
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
AdvertisementReadResult<SamplePlatform>::RetryStatus::TOO_SOON);
AdvertisementReadResult<TestPlatform>::RetryStatus::TOO_SOON);
}
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoffMax) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
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.
@@ -151,11 +135,11 @@ TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoffMax) {
absl::SleepFor(kAdvertisementMaxBackoffDuration);
ASSERT_EQ(advertisement_read_result.evaluateRetryStatus(),
AdvertisementReadResult<SamplePlatform>::RetryStatus::RETRY);
AdvertisementReadResult<TestPlatform>::RetryStatus::RETRY);
}
TEST(AdvertisementReadResultTest, GetDurationSinceRead) {
AdvertisementReadResult<SamplePlatform> advertisement_read_result;
AdvertisementReadResult<TestPlatform> advertisement_read_result;
advertisement_read_result.recordLastReadStatus(/* is_success= */ true);
std::int64_t sleepTime = 420;
+2 -2
View File
@@ -474,8 +474,8 @@ void BLEV2<Platform>::stopScanning() {
// TODO(b/112199086) Change to RecurringCancelableAlarm
template <typename Platform>
Ptr<CancelableAlarm<Platform>> BLEV2<Platform>::createOnLostAlarm() {
return Ptr<CancelableAlarm<Platform>>();
Ptr<CancelableAlarm> BLEV2<Platform>::createOnLostAlarm() {
return Ptr<CancelableAlarm>();
}
// Returns true if the device is currently accepting incoming BLE socket
+3 -3
View File
@@ -180,7 +180,7 @@ class BLEV2 {
struct ScanningInfo {
ScanningInfo(const string& service_id,
Ptr<ScanCallbackFacade> scan_callback_facade,
Ptr<CancelableAlarm<Platform>> on_lost_alarm)
Ptr<CancelableAlarm> on_lost_alarm)
: service_id(service_id),
scan_callback_facade(scan_callback_facade),
on_lost_alarm(on_lost_alarm) {}
@@ -191,7 +191,7 @@ class BLEV2 {
const string service_id;
ScopedPtr<Ptr<ScanCallbackFacade>> scan_callback_facade;
// TODO(ahlee): Change to recurring cancelable alarm
ScopedPtr<Ptr<CancelableAlarm<Platform>>> on_lost_alarm;
ScopedPtr<Ptr<CancelableAlarm>> on_lost_alarm;
};
struct AdvertisingInfo {
@@ -250,7 +250,7 @@ class BLEV2 {
Ptr<BLEPeripheralV2> ble_peripheral,
ConstPtr<BLEAdvertisementData> advertisement_data);
void processOnLostTimeout();
Ptr<CancelableAlarm<Platform>> createOnLostAlarm();
Ptr<CancelableAlarm> createOnLostAlarm();
bool isAdvertisementGattServerRunning();
bool startAdvertisementGattServer(const string& service_id,
@@ -14,7 +14,7 @@
#include "core/internal/mediums/lost_entity_tracker.h"
#include "platform/impl/default/default_platform.h"
#include "platform/api/platform.h"
#include "gtest/gtest.h"
namespace location {
@@ -23,6 +23,8 @@ namespace connections {
namespace mediums {
namespace {
using TestPlatform = platform::ImplementationPlatform;
struct TestEntity {
int id;
@@ -32,7 +34,7 @@ struct TestEntity {
};
TEST(LostEntityTracker, NoEntitiesLost) {
LostEntityTracker<DefaultPlatform, TestEntity> lost_entity_tracker;
LostEntityTracker<TestPlatform, TestEntity> lost_entity_tracker;
ScopedPtr<ConstPtr<TestEntity> > entity_1(MakeConstPtr(new TestEntity(1)));
ScopedPtr<ConstPtr<TestEntity> > entity_2(MakeConstPtr(new TestEntity(2)));
ScopedPtr<ConstPtr<TestEntity> > entity_3(MakeConstPtr(new TestEntity(3)));
@@ -55,7 +57,7 @@ TEST(LostEntityTracker, NoEntitiesLost) {
}
TEST(LostEntityTracker, AllEntitiesLost) {
LostEntityTracker<DefaultPlatform, TestEntity> lost_entity_tracker;
LostEntityTracker<TestPlatform, TestEntity> lost_entity_tracker;
ScopedPtr<ConstPtr<TestEntity> > entity_1(MakeConstPtr(new TestEntity(1)));
ScopedPtr<ConstPtr<TestEntity> > entity_2(MakeConstPtr(new TestEntity(2)));
ScopedPtr<ConstPtr<TestEntity> > entity_3(MakeConstPtr(new TestEntity(3)));
@@ -69,7 +71,7 @@ TEST(LostEntityTracker, AllEntitiesLost) {
ASSERT_TRUE(lost_entity_tracker.computeLostEntities().empty());
// Go through a round without rediscovering any entities.
typename LostEntityTracker<DefaultPlatform, TestEntity>::EntitySet
typename LostEntityTracker<TestPlatform, TestEntity>::EntitySet
lost_entities = lost_entity_tracker.computeLostEntities();
ASSERT_TRUE(lost_entities.find(entity_1.get()) != lost_entities.end());
ASSERT_TRUE(lost_entities.find(entity_2.get()) != lost_entities.end());
@@ -77,7 +79,7 @@ TEST(LostEntityTracker, AllEntitiesLost) {
}
TEST(LostEntityTracker, SomeEntitiesLost) {
LostEntityTracker<DefaultPlatform, TestEntity> lost_entity_tracker;
LostEntityTracker<TestPlatform, TestEntity> lost_entity_tracker;
ScopedPtr<ConstPtr<TestEntity> > entity_1(MakeConstPtr(new TestEntity(1)));
ScopedPtr<ConstPtr<TestEntity> > entity_2(MakeConstPtr(new TestEntity(2)));
ScopedPtr<ConstPtr<TestEntity> > entity_3(MakeConstPtr(new TestEntity(3)));
@@ -94,7 +96,7 @@ TEST(LostEntityTracker, SomeEntitiesLost) {
// was lost after the check.
lost_entity_tracker.recordFoundEntity(entity_1.get());
lost_entity_tracker.recordFoundEntity(entity_3.get());
typename LostEntityTracker<DefaultPlatform, TestEntity>::EntitySet
typename LostEntityTracker<TestPlatform, TestEntity>::EntitySet
lost_entities = lost_entity_tracker.computeLostEntities();
ASSERT_TRUE(lost_entities.find(entity_1.get()) == lost_entities.end());
ASSERT_TRUE(lost_entities.find(entity_2.get()) != lost_entities.end());
@@ -102,7 +104,7 @@ TEST(LostEntityTracker, SomeEntitiesLost) {
}
TEST(LostEntityTracker, SameEntityMultipleCopies) {
LostEntityTracker<DefaultPlatform, TestEntity> lost_entity_tracker;
LostEntityTracker<TestPlatform, TestEntity> lost_entity_tracker;
ScopedPtr<ConstPtr<TestEntity> > entity_1(MakeConstPtr(new TestEntity(1)));
ScopedPtr<ConstPtr<TestEntity> > entity_1_copy(
MakeConstPtr(new TestEntity(1)));
@@ -121,7 +123,7 @@ TEST(LostEntityTracker, SameEntityMultipleCopies) {
// Go through a round without rediscovering any entities and verify that we
// lost an entity equivalent to both copies of it.
typename LostEntityTracker<DefaultPlatform, TestEntity>::EntitySet
typename LostEntityTracker<TestPlatform, TestEntity>::EntitySet
lost_entities = lost_entity_tracker.computeLostEntities();
ASSERT_EQ(lost_entities.size(), 1);
ASSERT_TRUE(lost_entities.find(entity_1.get()) != lost_entities.end());
+7 -1
View File
@@ -24,7 +24,8 @@ Mediums<Platform>::Mediums()
bluetooth_classic_(
new BluetoothClassic<Platform>(bluetooth_radio_.get())),
ble_(new BLE<Platform>(bluetooth_radio_.get())),
ble_v2_(new mediums::BLEV2<Platform>(bluetooth_radio_.get())) {}
ble_v2_(new mediums::BLEV2<Platform>(bluetooth_radio_.get())),
wifi_lan_(new mediums::WifiLan<Platform>()) {}
template <typename Platform>
Mediums<Platform>::~Mediums() {
@@ -51,6 +52,11 @@ Ptr<mediums::BLEV2<Platform> > Mediums<Platform>::bleV2() const {
return ble_v2_.get();
}
template <typename Platform>
Ptr<mediums::WifiLan<Platform> > Mediums<Platform>::wifi_lan() const {
return wifi_lan_.get();
}
} // namespace connections
} // namespace nearby
} // namespace location
+4
View File
@@ -19,6 +19,7 @@
#include "core/internal/mediums/ble_v2.h"
#include "core/internal/mediums/bluetooth_classic.h"
#include "core/internal/mediums/bluetooth_radio.h"
#include "core/internal/mediums/wifi_lan.h"
#include "platform/ptr.h"
namespace location {
@@ -41,6 +42,8 @@ class Mediums {
Ptr<BLE<Platform> > ble() const;
// Returns a handle to V2 of the Bluetooth Low Energy (BLE) medium.
Ptr<mediums::BLEV2<Platform> > bleV2() const;
// Returns a handle to the Wifi-Lan medium.
Ptr<mediums::WifiLan<Platform> > wifi_lan() const;
private:
// The order of declaration is critical for both construction and
@@ -55,6 +58,7 @@ class Mediums {
ScopedPtr<Ptr<BluetoothClassic<Platform> > > bluetooth_classic_;
ScopedPtr<Ptr<BLE<Platform> > > ble_;
ScopedPtr<Ptr<mediums::BLEV2<Platform> > > ble_v2_;
ScopedPtr<Ptr<mediums::WifiLan<Platform> > > wifi_lan_;
};
} // namespace connections
+22
View File
@@ -14,9 +14,11 @@
#include "core/internal/mediums/utils.h"
#include <cstdint>
#include <sstream>
#include "platform/exception.h"
#include "platform/prng.h"
#include "absl/strings/escaping.h"
namespace location {
@@ -62,6 +64,26 @@ ConstPtr<ByteArray> Utils::legacySha256HashOnlyForPrinting(
return Utils::sha256Hash(hash_utils, formatted_hex_byte_array.get(), length);
}
ConstPtr<ByteArray> Utils::generateRandomBytes(size_t length) {
Prng rng;
std::string data;
data.reserve(length);
// Adds 4 random bytes per iteration.
while (length > 0) {
std::uint32_t val = rng.nextUInt32();
for (int i = 0; i < 4; i++) {
data += val & 0xFF;
val >>= 8;
length--;
if (!length) break;
}
}
return MakeConstPtr(new ByteArray(data));
}
std::string Utils::bytesToPrintableHexString(ConstPtr<ByteArray> bytes) {
std::string hex_string(
absl::BytesToHexString(std::string(bytes->getData(), bytes->size())));
+2
View File
@@ -35,6 +35,8 @@ class Utils {
static ConstPtr<ByteArray> legacySha256HashOnlyForPrinting(
Ptr<HashUtils> hash_utils, ConstPtr<ByteArray> source, size_t length);
static ConstPtr<ByteArray> generateRandomBytes(size_t length);
private:
static std::string bytesToPrintableHexString(ConstPtr<ByteArray> bytes);
};
+91
View File
@@ -0,0 +1,91 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cc_library(
name = "webrtc",
hdrs = [
"webrtc_socket.cc",
"webrtc_socket.h",
],
deps = [
"//platform:utils",
"//platform/api",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_test(
name = "webrtc_test",
srcs = ["webrtc_socket_test.cc"],
deps = [
":webrtc",
"//platform:types",
"//platform/api",
"//platform/impl/g3", # buildcleaner: keep
"//testing/base/public:gunit_main",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_library(
name = "peer_id",
srcs = ["peer_id.cc"],
hdrs = ["peer_id.h"],
deps = [
"//core/internal/mediums:utils",
"//platform:types",
"//platform/api",
"//platform/port:string",
"//absl/strings",
],
)
cc_library(
name = "signaling_frames",
srcs = ["signaling_frames.cc"],
hdrs = ["signaling_frames.h"],
deps = [
":peer_id",
"//platform:types",
"//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_test(
name = "peer_id_test",
srcs = ["peer_id_test.cc"],
deps = [
":peer_id",
"//platform:types",
"//platform/api",
"//platform/impl/g3", # buildcleaner: keep
"//testing/base/public:gunit_main",
"//absl/strings",
],
)
cc_test(
name = "signaling_frames_test",
srcs = ["signaling_frames_test.cc"],
deps = [
":peer_id",
":signaling_frames",
"//platform:types",
"//platform/impl/g3", # buildcleaner: keep
"//net/proto2/public:proto2",
"//testing/base/public:gunit_main",
"//webrtc/pc:peerconnection", # buildcleaner: keep
],
)
@@ -0,0 +1,55 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/peer_id.h"
#include <sstream>
#include "core/internal/mediums/utils.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
constexpr int kPeerIdLength = 64;
std::string BytesToStringUppercase(ConstPtr<ByteArray> bytes) {
std::string hex_string(
absl::BytesToHexString(std::string(bytes->getData(), bytes->size())));
absl::AsciiStrToUpper(&hex_string);
return hex_string;
}
} // namespace
ConstPtr<PeerId> PeerId::FromRandom(Ptr<HashUtils> hash_utils) {
return FromSeed(Utils::generateRandomBytes(kPeerIdLength), hash_utils);
}
ConstPtr<PeerId> PeerId::FromSeed(ConstPtr<ByteArray> seed,
Ptr<HashUtils> hash_utils) {
ScopedPtr<ConstPtr<ByteArray>> full_hash(
Utils::sha256Hash(hash_utils, seed, kPeerIdLength));
ScopedPtr<ConstPtr<ByteArray>> hashedSeed(
MakeConstPtr(new ByteArray(full_hash->getData(), kPeerIdLength / 2)));
return MakeConstPtr(new PeerId(BytesToStringUppercase(hashedSeed.get())));
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,50 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
#include "platform/api/hash_utils.h"
#include "platform/byte_array.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// PeerId is used as an identifier to exchange SDP messages to establish WebRTC
// p2p connection.
class PeerId {
public:
explicit PeerId(const string& id) : id_(id) {}
~PeerId() = default;
static ConstPtr<PeerId> FromRandom(Ptr<HashUtils> hash_utils);
static ConstPtr<PeerId> FromSeed(ConstPtr<ByteArray> seed,
Ptr<HashUtils> hash_utils);
const string& GetId() const { return id_; }
private:
const string id_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
@@ -0,0 +1,90 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/peer_id.h"
#include "platform/api/hash_utils.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
class MockHashUtils : public HashUtils {
public:
MOCK_METHOD(ConstPtr<ByteArray>, md5, (const std::string& input), (override));
MOCK_METHOD(ConstPtr<ByteArray>, sha256, (const std::string& input),
(override));
};
} // namespace
TEST(PeerIdTest, GenerateRandomPeerId) {
// These are actual SHA-256 values for |seed| = "seed".
std::string hashed_output =
"19b25856e1c150ca834cffc8b59b23adbd0ec0389e58eb22b3b64768098d002b";
std::string expected_peer_id =
"19B25856E1C150CA834CFFC8B59B23ADBD0EC0389E58EB22B3B64768098D002B";
Ptr<testing::NiceMock<MockHashUtils>> mock_hash_utils(
MakePtr(new MockHashUtils()));
ON_CALL(*mock_hash_utils.get(), sha256(testing::_))
.WillByDefault(testing::Return(
MakeConstPtr(new ByteArray(absl::HexStringToBytes(hashed_output)))));
EXPECT_CALL(*mock_hash_utils.get(), sha256(testing::_));
ConstPtr<PeerId> peer_id = PeerId::FromRandom(mock_hash_utils);
ASSERT_EQ(64, peer_id->GetId().size());
ASSERT_EQ(expected_peer_id, peer_id->GetId());
}
TEST(PeerIdTest, GenerateFromSeed) {
// Values calculated by running actual SHA-256 hash on |seed|.
std::string seed = "sesdfed";
std::string hashed_output =
"19b25856e1c150ca834cffc8b59b23adbd0ec0389e58eb22b3b64768098d002b";
std::string expected_peer_id =
"19B25856E1C150CA834CFFC8B59B23ADBD0EC0389E58EB22B3B64768098D002B";
Ptr<testing::NiceMock<MockHashUtils>> mock_hash_utils(
MakePtr(new MockHashUtils()));
ON_CALL(*mock_hash_utils.get(), sha256(testing::Eq(seed)))
.WillByDefault(testing::Return(
MakeConstPtr(new ByteArray(absl::HexStringToBytes(hashed_output)))));
EXPECT_CALL(*mock_hash_utils.get(), sha256(testing::Eq(seed)));
ConstPtr<PeerId> peer_id =
PeerId::FromSeed(MakeConstPtr(new ByteArray(seed)), mock_hash_utils);
ASSERT_EQ(64, peer_id->GetId().size());
ASSERT_EQ(expected_peer_id, peer_id->GetId());
}
TEST(PeerIdTest, GetId) {
const std::string id = "this_is_a_test";
PeerId peer_id(id);
ASSERT_EQ(id, peer_id.GetId());
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,139 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/signaling_frames.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
using WebRtcSignalingFrame = location::nearby::mediums::WebRtcSignalingFrame;
namespace {
ConstPtr<ByteArray> FrameToByteArray(
const WebRtcSignalingFrame& signaling_frame) {
std::string message;
signaling_frame.SerializeToString(&message);
return MakeConstPtr(new ByteArray(message.c_str(), message.size()));
}
void SetSenderId(ConstPtr<PeerId> sender_id, WebRtcSignalingFrame& frame) {
frame.mutable_sender_id()->set_id(sender_id->GetId());
}
ConstPtr<webrtc::IceCandidateInterface> DecodeIceCandidate(
const location::nearby::mediums::IceCandidate& ice_candidate_proto) {
webrtc::SdpParseError error;
return ConstPtr<webrtc::IceCandidateInterface>(webrtc::CreateIceCandidate(
ice_candidate_proto.sdp_mid(), ice_candidate_proto.sdp_m_line_index(),
ice_candidate_proto.sdp(), &error));
}
} // namespace
ConstPtr<ByteArray> EncodeReadyForSignalingPoke(ConstPtr<PeerId> sender_id) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::READY_FOR_SIGNALING_POKE_TYPE);
SetSenderId(sender_id, signaling_frame);
signaling_frame.mutable_ready_for_signaling_poke();
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeOffer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& offer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::OFFER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string offer_str;
offer.ToString(&offer_str);
signaling_frame.mutable_offer()
->mutable_session_description()
->set_description(offer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeAnswer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& answer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ANSWER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string answer_str;
answer.ToString(&answer_str);
signaling_frame.mutable_answer()
->mutable_session_description()
->set_description(answer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeIceCandidates(
ConstPtr<PeerId> sender_id,
const std::vector<location::nearby::mediums::IceCandidate>&
ice_candidates) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ICE_CANDIDATES_TYPE);
SetSenderId(sender_id, signaling_frame);
for (const auto& ice_candidate : ice_candidates) {
*signaling_frame.mutable_ice_candidates()->add_ice_candidates() =
ice_candidate;
}
return FrameToByteArray(std::move(signaling_frame));
}
Ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const WebRtcSignalingFrame& frame) {
return MakePtr(webrtc::CreateSessionDescription(
webrtc::SdpType::kOffer,
frame.offer().session_description().description())
.release());
}
Ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const WebRtcSignalingFrame& frame) {
return MakePtr(webrtc::CreateSessionDescription(
webrtc::SdpType::kAnswer,
frame.answer().session_description().description())
.release());
}
std::vector<ConstPtr<webrtc::IceCandidateInterface>> DecodeIceCandidates(
const WebRtcSignalingFrame& frame) {
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
for (const auto& candidate : frame.ice_candidates().ice_candidates()) {
ice_candidates.push_back(DecodeIceCandidate(candidate));
}
return ice_candidates;
}
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidateInterface& ice_candidate) {
std::string sdp;
ice_candidate.ToString(&sdp);
location::nearby::mediums::IceCandidate ice_candidate_proto;
ice_candidate_proto.set_sdp(sdp);
ice_candidate_proto.set_sdp_mid(ice_candidate.sdp_mid());
ice_candidate_proto.set_sdp_m_line_index(ice_candidate.sdp_mline_index());
return ice_candidate_proto;
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,63 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#include <vector>
#include "core/internal/mediums/webrtc/peer_id.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h"
#include "webrtc/api/peer_connection_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
ConstPtr<ByteArray> EncodeReadyForSignalingPoke(ConstPtr<PeerId> sender_id);
ConstPtr<ByteArray> EncodeOffer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& offer);
ConstPtr<ByteArray> EncodeAnswer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& answer);
ConstPtr<ByteArray> EncodeIceCandidates(
ConstPtr<PeerId> sender_id,
const std::vector<location::nearby::mediums::IceCandidate>& ice_candidates);
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidateInterface& ice_candidate);
Ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
Ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
std::vector<ConstPtr<webrtc::IceCandidateInterface>> DecodeIceCandidates(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
@@ -0,0 +1,198 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/signaling_frames.h"
#include <memory>
#include "core/internal/mediums/webrtc/peer_id.h"
#include "platform/ptr.h"
#include "net/proto2/public/text_format.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
namespace {
const char kSampleSdp[] =
"v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 "
"0\r\na=msid-semantic: WMS\r\n";
const char kIceCandidateSdp1[] =
"a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host";
const char kIceCandidateSdp2[] =
"a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr";
const char kIceSdpMid[] = "data";
const int kIceSdpMLineIndex = 0;
const char kOfferProto[] = R"(
sender_id { id: "abc" }
type: OFFER_TYPE
offer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kAnswerProto[] = R"(
sender_id { id: "abc" }
type: ANSWER_TYPE
answer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kIceCandidatesProto[] = R"(
sender_id { id: "abc" }
type: ICE_CANDIDATES_TYPE
ice_candidates {
ice_candidates {
sdp: "candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
ice_candidates {
sdp: "candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
}
)";
} // namespace
TEST(SignalingFramesTest, SignalingPoke) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
ConstPtr<ByteArray> encoded_poke = EncodeReadyForSignalingPoke(sender_id);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_poke->getData(), encoded_poke->size()));
EXPECT_THAT(frame, testing::EqualsProto(R"(
sender_id { id: "abc" }
type: READY_FOR_SIGNALING_POKE_TYPE
ready_for_signaling_poke {}
)"));
}
TEST(SignalingFramesTest, EncodeValidOffer) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
std::unique_ptr<webrtc::SessionDescriptionInterface> offer =
webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, kSampleSdp);
ConstPtr<ByteArray> encoded_offer = EncodeOffer(sender_id, *offer);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_offer->getData(), encoded_offer->size()));
EXPECT_THAT(frame, testing::EqualsProto(kOfferProto));
}
TEST(SignalingFramesTest, DecodeValidOffer) {
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kOfferProto, &frame);
Ptr<webrtc::SessionDescriptionInterface> decoded_offer = DecodeOffer(frame);
EXPECT_EQ(webrtc::SdpType::kOffer, decoded_offer->GetType());
std::string description;
decoded_offer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidAnswer) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
std::unique_ptr<webrtc::SessionDescriptionInterface> answer =
webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, kSampleSdp);
ConstPtr<ByteArray> encoded_answer = EncodeAnswer(sender_id, *answer);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_answer->getData(), encoded_answer->size()));
EXPECT_THAT(frame, testing::EqualsProto(kAnswerProto));
}
TEST(SignalingFramesTest, DecodeValidAnswer) {
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kAnswerProto, &frame);
Ptr<webrtc::SessionDescriptionInterface> decoded_answer = DecodeAnswer(frame);
EXPECT_EQ(webrtc::SdpType::kAnswer, decoded_answer->GetType());
std::string description;
decoded_answer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidIceCandidates) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
webrtc::SdpParseError error;
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
std::vector<location::nearby::mediums::IceCandidate> encoded_candidates_vec;
for (const auto& ice_candidate : ice_candidates) {
encoded_candidates_vec.push_back(EncodeIceCandidate(*ice_candidate.get()));
}
ConstPtr<ByteArray> encoded_candidates =
EncodeIceCandidates(sender_id, encoded_candidates_vec);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_candidates->getData(), encoded_candidates->size()));
EXPECT_THAT(frame, testing::EqualsProto(kIceCandidatesProto));
}
TEST(SignalingFramesTest, DecodeValidIceCandidates) {
webrtc::SdpParseError error;
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
std::vector<location::nearby::mediums::IceCandidate> encoded_candidates_vec;
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kIceCandidatesProto, &frame);
std::vector<ConstPtr<webrtc::IceCandidateInterface>> decoded_candidates =
DecodeIceCandidates(frame);
ASSERT_EQ(2u, decoded_candidates.size());
for (int i = 0; i < static_cast<int>(decoded_candidates.size()); i++) {
EXPECT_TRUE(ice_candidates[i]->candidate().IsEquivalent(
decoded_candidates[i]->candidate()));
EXPECT_EQ(ice_candidates[i]->sdp_mid(), decoded_candidates[i]->sdp_mid());
EXPECT_EQ(ice_candidates[i]->sdp_mline_index(),
decoded_candidates[i]->sdp_mline_index());
}
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,153 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/webrtc_socket.h"
#include "platform/synchronized.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// OutputStreamImpl
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::write(
ConstPtr<ByteArray> data) {
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
if (scoped_data->size() > kMaxDataSize) {
NEARBY_LOG(WARNING, "Sending data larger than 1MB");
return Exception::IO;
}
socket_->BlockUntilSufficientSpaceInBuffer(scoped_data->size());
if (socket_->IsClosed()) {
NEARBY_LOG(WARNING, "Tried sending message while socket is closed");
return Exception::IO;
}
if (!socket_->SendMessage(scoped_data.release())) {
return Exception::IO;
}
return Exception::NONE;
}
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::flush() {
// Java implementation is empty.
return Exception::NONE;
}
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::close() {
socket_->close();
return Exception::NONE;
}
// WebRtcSocket
template <typename Platform>
WebRtcSocket<Platform>::WebRtcSocket(
const string& name,
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
: name_(name),
data_channel_(std::move(data_channel)),
pipe_(MakeRefCountedPtr(new Pipe())),
incoming_data_piped_input_stream_(Pipe::createInputStream(pipe_)),
incoming_data_piped_output_stream_(Pipe::createOutputStream(pipe_)),
output_stream_(MakePtr(new OutputStreamImpl(this))),
closed_(Platform::createAtomicBoolean(false)),
backpressure_lock_(Platform::createLock()),
buffer_variable_(
Platform::createConditionVariable(backpressure_lock_.get())) {}
template <typename Platform>
Ptr<InputStream> WebRtcSocket<Platform>::getInputStream() {
return incoming_data_piped_input_stream_.get();
}
template <typename Platform>
Ptr<OutputStream> WebRtcSocket<Platform>::getOutputStream() {
return output_stream_.get();
}
template <typename Platform>
void WebRtcSocket<Platform>::close() {
if (IsClosed()) return;
closed_->set(true);
incoming_data_piped_output_stream_->close();
incoming_data_piped_input_stream_->close();
data_channel_->Close();
WakeUpWriter();
if (!socket_closed_listener_.isNull()) {
socket_closed_listener_->OnSocketClosed();
}
}
template <typename Platform>
void WebRtcSocket<Platform>::NotifyDataChannelMsgReceived(
ConstPtr<ByteArray> message) {
Exception::Value exception =
incoming_data_piped_output_stream_->write(message);
if (exception != Exception::NONE) close();
exception = incoming_data_piped_output_stream_->flush();
if (exception != Exception::NONE) close();
}
template <typename Platform>
void WebRtcSocket<Platform>::NotifyDataChannelBufferedAmountChanged() {
WakeUpWriter();
}
template <typename Platform>
bool WebRtcSocket<Platform>::SendMessage(ConstPtr<ByteArray> data) {
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
return data_channel_->Send(webrtc::DataBuffer(
std::string(scoped_data->getData(), scoped_data->size())));
}
template <typename Platform>
bool WebRtcSocket<Platform>::IsClosed() {
return closed_->get();
}
template <typename Platform>
void WebRtcSocket<Platform>::WakeUpWriter() {
Synchronized s(backpressure_lock_.get());
buffer_variable_->notify();
}
template <typename Platform>
void WebRtcSocket<Platform>::SetOnSocketClosedListener(
Ptr<SocketClosedListener> listener) {
socket_closed_listener_ = listener;
}
template <typename Platform>
void WebRtcSocket<Platform>::BlockUntilSufficientSpaceInBuffer(int length) {
Synchronized s(backpressure_lock_.get());
while (!IsClosed() &&
(data_channel_->buffered_amount() + length > kMaxDataSize)) {
// TODO(himanshujaju): Add wait with timeout.
buffer_variable_->wait();
}
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,118 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
#include "platform/api/atomic_boolean.h"
#include "platform/api/input_stream.h"
#include "platform/api/output_stream.h"
#include "platform/api/socket.h"
#include "platform/pipe.h"
#include "webrtc/api/data_channel_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// Maximum data size: 1 MB
constexpr int kMaxDataSize = 1 * 1024 * 1024;
// Defines the Socket implementation specific to WebRTC, which uses the WebRTC
// data channel to send and receive messages.
//
// Messages are buffered here to prevent the data channel from overflowing,
// which could lead to data loss.
template <typename Platform>
class WebRtcSocket : public Socket {
public:
WebRtcSocket(const string& name,
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
~WebRtcSocket() override = default;
WebRtcSocket(const WebRtcSocket& other) = delete;
WebRtcSocket& operator=(const WebRtcSocket& other) = delete;
// Overrides for location::nearby::Socket:
Ptr<InputStream> getInputStream() override;
Ptr<OutputStream> getOutputStream() override;
void close() override;
// Callback from WebRTC data channel when new message has been received from
// the remote.
void NotifyDataChannelMsgReceived(ConstPtr<ByteArray> message);
// Callback from WebRTC data channel that the buffered data amount has
// changed.
void NotifyDataChannelBufferedAmountChanged();
// Listener class the gets called when the socket is closed.
class SocketClosedListener {
public:
virtual ~SocketClosedListener() = default;
virtual void OnSocketClosed() = 0;
};
void SetOnSocketClosedListener(Ptr<SocketClosedListener> listener);
private:
class OutputStreamImpl : public OutputStream {
public:
explicit OutputStreamImpl(WebRtcSocket<Platform>* const socket)
: socket_(socket) {}
~OutputStreamImpl() override = default;
OutputStreamImpl(const OutputStreamImpl& other) = delete;
OutputStreamImpl& operator=(const OutputStreamImpl& other) = delete;
// OutputStream:
Exception::Value write(ConstPtr<ByteArray> data) override;
Exception::Value flush() override;
Exception::Value close() override;
private:
// |this| OutputStreamImpl is owned by |socket_|.
WebRtcSocket<Platform>* const socket_;
};
void WakeUpWriter();
bool IsClosed();
bool SendMessage(ConstPtr<ByteArray> data);
void BlockUntilSufficientSpaceInBuffer(int length);
string name_;
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
Ptr<Pipe> pipe_;
ScopedPtr<Ptr<InputStream>> incoming_data_piped_input_stream_;
ScopedPtr<Ptr<OutputStream>> incoming_data_piped_output_stream_;
ScopedPtr<Ptr<OutputStream>> output_stream_;
ScopedPtr<Ptr<AtomicBoolean>> closed_;
Ptr<SocketClosedListener> socket_closed_listener_;
ScopedPtr<Ptr<Lock>> backpressure_lock_;
ScopedPtr<Ptr<ConditionVariable>> buffer_variable_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#include "core/internal/mediums/webrtc/webrtc_socket.cc"
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
@@ -0,0 +1,169 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/webrtc/webrtc_socket.h"
#include "platform/api/platform.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "webrtc/api/data_channel_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
using TestPlatform = platform::ImplementationPlatform;
const char kSocketName[] = "TestSocket";
class MockDataChannel
: public rtc::RefCountedObject<webrtc::DataChannelInterface> {
public:
MOCK_METHOD(void, RegisterObserver, (webrtc::DataChannelObserver*));
MOCK_METHOD(void, UnregisterObserver, ());
MOCK_METHOD(std::string, label, (), (const));
MOCK_METHOD(bool, reliable, (), (const));
MOCK_METHOD(int, id, (), (const));
MOCK_METHOD(DataState, state, (), (const));
MOCK_METHOD(uint32_t, messages_sent, (), (const));
MOCK_METHOD(uint64_t, bytes_sent, (), (const));
MOCK_METHOD(uint32_t, messages_received, (), (const));
MOCK_METHOD(uint64_t, bytes_received, (), (const));
MOCK_METHOD(uint64_t, buffered_amount, (), (const));
MOCK_METHOD(void, Close, ());
MOCK_METHOD(bool, Send, (const webrtc::DataBuffer&));
};
} // namespace
class MockSocketClosedListener
: public WebRtcSocket<TestPlatform>::SocketClosedListener {
public:
MOCK_METHOD(void, OnSocketClosed, ());
};
TEST(WebRtcSocketTest, ReadFromSocket) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.NotifyDataChannelMsgReceived(kMessage);
ExceptionOr<ConstPtr<ByteArray>> result =
webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), kMessage);
}
TEST(WebRtcSocketTest, ReadMultipleMessages) {
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.NotifyDataChannelMsgReceived(MakeConstPtr(new ByteArray("Me")));
webrtc_socket.NotifyDataChannelMsgReceived(
MakeConstPtr(new ByteArray("ssa")));
webrtc_socket.NotifyDataChannelMsgReceived(MakeConstPtr(new ByteArray("ge")));
ExceptionOr<ConstPtr<ByteArray>> result;
// This behaviour is different from the Java code
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "Me");
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "ssa");
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "ge");
}
TEST(WebRtcSocketTest, WriteToSocket) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_))
.WillRepeatedly(testing::Return(true));
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::NONE);
}
TEST(WebRtcSocketTest, SendDataBiggerThanMax) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray(kMaxDataSize + 1));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, WriteToDataChannelFails) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(false));
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, Close) {
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
ScopedPtr<Ptr<MockSocketClosedListener>> mock_listener(
MakePtr(new MockSocketClosedListener()));
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.SetOnSocketClosedListener(mock_listener.get());
EXPECT_CALL(*mock_listener, OnSocketClosed());
EXPECT_CALL(*mock_data_channel, Close());
webrtc_socket.close();
}
TEST(WebRtcSocketTest, WriteOnClosedChannel) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.close();
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, ReadFromClosedChannel) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(true));
webrtc_socket.getOutputStream()->write(kMessage);
webrtc_socket.close();
EXPECT_EQ(webrtc_socket.getInputStream()->read().exception(), Exception::IO);
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
+227
View File
@@ -0,0 +1,227 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/mediums/wifi_lan.h"
#include "platform/synchronized.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
template <typename Platform>
WifiLan<Platform>::WifiLan()
: lock_(Platform::createLock()),
wifi_lan_medium_(Platform::createWifiLanMedium()) {}
template <typename Platform>
bool WifiLan<Platform>::IsAvailable() {
Synchronized s(lock_.get());
return !wifi_lan_medium_.isNull();
}
template <typename Platform>
bool WifiLan<Platform>::StartAdvertising(
absl::string_view service_id,
absl::string_view wifi_lan_service_info_name) {
Synchronized s(lock_.get());
if (!IsAvailable()) {
return false;
}
// TODO(b/149806065): Implements platform wifi-lan medium.
// wifi_lan_medium_->StartAdvertising(service_id,
// wifi_lan_service_info_name));
advertising_info_.service_id.assign(service_id.data());
return false;
}
template <typename Platform>
void WifiLan<Platform>::StopAdvertising(absl::string_view service_id) {
Synchronized s(lock_.get());
if (!IsAdvertising()) {
return;
}
// TODO(b/149806065): Implements platform wifi-lan medium.
// wifi_lan_medium_->StopAdvertising(advertising_info_.service_id);
// Reset our bundle of advertising state to mark that we're no longer
// advertising.
advertising_info_.service_id.clear();
}
template <typename Platform>
bool WifiLan<Platform>::IsAdvertising() {
Synchronized s(lock_.get());
return !advertising_info_.service_id.empty();
}
template <typename Platform>
bool WifiLan<Platform>::StartDiscovery(
absl::string_view service_id,
Ptr<DiscoveredServiceCallback> discovered_service_callback) {
Synchronized s(lock_.get());
if (discovered_service_callback.isNull() || service_id.empty()) {
// TODO(b/149806065): logger.atSevere().log("Refusing to start WifiLan
// discovering because a null parameter was passed in.");
return false;
}
if (IsDiscovering(service_id)) {
// TODO(b/149806065): logger.atSevere().log("Refusing to start WifiLan
// discovering because we are already discovering.");
return false;
}
if (!IsAvailable()) {
// TODO(b/149806065): logger.atSevere().log("Can't start WifiLan discovering
// because WifiLan isn't available.");
return false;
}
// Avoid leaks.
ScopedPtr<Ptr<DiscoveredServiceCallbackBridge>>
scoped_discovered_service_callback_bridge(
new DiscoveredServiceCallbackBridge(discovered_service_callback));
// TODO(b/149806065): Implements platform wifi-lan medium.
// A possible implementation is:
// wifi_lan_medium_->StartDiscovery(
// service_id, Ptr<DiscoveredServiceCallbackBridge>(
// discovered_service_callback_bridge.release()));
discovering_info_.service_id.assign(service_id.data());
return false;
}
template <typename Platform>
void WifiLan<Platform>::StopDiscovery(absl::string_view service_id) {
Synchronized s(lock_.get());
if (!IsDiscovering(service_id)) {
// TODO(b/149806065): logger.atDebug().log("Can't turn off WifiLan
// discovering because we never started discovering.");
return;
}
// TODO(b/149806065): Implements platform wifi-lan medium.
// wifi_lan_medium_->StopDiscovery(discovering_info_.service_id);
// Reset our bundle of scanning state to mark that we're no longer scanning.
discovering_info_.service_id.clear();
}
template <typename Platform>
bool WifiLan<Platform>::IsDiscovering(absl::string_view service_id) {
Synchronized s(lock_.get());
return !discovering_info_.service_id.empty();
}
template <typename Platform>
bool WifiLan<Platform>::StartAcceptingConnections(
absl::string_view service_id,
Ptr<AcceptedConnectionCallback> accepted_connection_callback) {
Synchronized s(lock_.get());
if (accepted_connection_callback.isNull() || service_id.empty()) {
// TODO(b/149806065): logger.atSevere().log("Refusing to start accepting
// WifiLan connections because a null parameter was passed in.");
return false;
}
if (IsAcceptingConnections(service_id)) {
// TODO(b/149806065): logger.atSevere().log("Refusing to start accepting
// WifiLan connections for %s because another WifiLan service socket is
// already in-progress.", service_id);
return false;
}
if (!IsAvailable()) {
// TODO(b/149806065): logger.atSevere().log("Can't start accepting WifiLan
// connections for %s because WifiLan isn't available.", serviceId);
return false;
}
ScopedPtr<Ptr<WifiLanAcceptedConnectionCallback>>
scoped_wifi_lan_accepted_connection_callback(
new WifiLanAcceptedConnectionCallback(
accepted_connection_callback));
// TODO(b/149806065): Implements platform wifi-lan medium.
// A possible implementation is:
// wifi_lan_medium_->StartAcceptingConnections(
// service_id, Ptr<WifiLanAcceptedConnectionCallback>(
// wifi_lan_accepted_connection_callback.release()));
accepting_connections_info_.service_id.assign(service_id.data());
return false;
}
template <typename Platform>
void WifiLan<Platform>::StopAcceptingConnections(absl::string_view service_id) {
Synchronized s(lock_.get());
if (!IsAcceptingConnections(service_id)) {
// TODO(b/149806065): logger.atDebug().log("Can't stop accepting WifiLan
// connections because it was never started.");
return;
}
// TODO(b/149806065): Implements platform wifi-lan medium.);
// A possible implementation is:
// wifi_lan_medium_->StopAcceptingConnections(
// accepting_connections_info_.service_id);
// Reset our bundle of accepting connections state to mark that we're no
// longer accepting connections.
accepting_connections_info_.service_id.clear();
}
template <typename Platform>
bool WifiLan<Platform>::IsAcceptingConnections(absl::string_view service_id) {
Synchronized s(lock_.get());
return !accepting_connections_info_.service_id.empty();
}
template <typename Platform>
Ptr<WifiLanSocket> WifiLan<Platform>::Connect(
Ptr<WifiLanService> wifi_lan_service, absl::string_view service_id) {
Synchronized s(lock_.get());
if (wifi_lan_service.isNull() || service_id.empty()) {
return Ptr<WifiLanSocket>();
}
if (!IsAvailable()) {
return Ptr<WifiLanSocket>();
}
// TODO(b/149806065): Implements platform wifi-lan medium.
// A possible implementation is:
// return wifi_lan_medium_->Connect(wifi_lan_service, service_id);
return Ptr<WifiLanSocket>();
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
+174
View File
@@ -0,0 +1,174 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_WIFI_LAN_H_
#define CORE_INTERNAL_MEDIUMS_WIFI_LAN_H_
#include <cstdint>
#include "platform/api/lock.h"
#include "platform/api/wifi_lan.h"
#include "platform/byte_array.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
class DiscoveredServiceCallback {
public:
virtual ~DiscoveredServiceCallback() = default;
virtual void OnServiceDiscovered(Ptr<WifiLanService> wifi_lan_service) = 0;
virtual void OnServiceLost(Ptr<WifiLanService> wifi_lan_service) = 0;
};
template <typename Platform>
class WifiLan {
public:
WifiLan();
virtual ~WifiLan() = default;
bool IsAvailable();
bool StartAdvertising(absl::string_view service_id,
absl::string_view wifi_lan_service_info_name);
void StopAdvertising(absl::string_view service_id);
bool IsAdvertising();
bool StartDiscovery(
absl::string_view service_id,
Ptr<DiscoveredServiceCallback> discovered_service_callback);
void StopDiscovery(absl::string_view service_id);
bool IsDiscovering(absl::string_view service_id);
class AcceptedConnectionCallback {
public:
virtual ~AcceptedConnectionCallback() = default;
virtual void OnConnectionAccepted(Ptr<WifiLanSocket> socket,
absl::string_view service_id) = 0;
};
bool StartAcceptingConnections(
absl::string_view service_id,
Ptr<AcceptedConnectionCallback> accepted_connection_callback);
void StopAcceptingConnections(absl::string_view service_id);
bool IsAcceptingConnections(absl::string_view service_id);
Ptr<WifiLanSocket> Connect(Ptr<WifiLanService> wifi_lan_service,
absl::string_view service_id);
private:
class DiscoveredServiceCallbackBridge
: public WifiLanMedium::DiscoveredServiceCallback {
public:
explicit DiscoveredServiceCallbackBridge(
Ptr<mediums::DiscoveredServiceCallback> discovered_service_callback)
: discovered_service_callback_(discovered_service_callback) {}
~DiscoveredServiceCallbackBridge() override = default;
void OnServiceDiscovered(Ptr<WifiLanService> wifi_lan_service) override {
discovered_service_callback_->OnServiceDiscovered(wifi_lan_service);
}
void OnServiceLost(Ptr<WifiLanService> wifi_lan_service) override {
discovered_service_callback_->OnServiceLost(wifi_lan_service);
}
private:
ScopedPtr<Ptr<mediums::DiscoveredServiceCallback>>
discovered_service_callback_;
};
class WifiLanAcceptedConnectionCallback
: public WifiLanMedium::AcceptedConnectionCallback {
public:
explicit WifiLanAcceptedConnectionCallback(
Ptr<WifiLan::AcceptedConnectionCallback> accepted_connection_callback)
: accepted_connection_callback_(accepted_connection_callback) {}
~WifiLanAcceptedConnectionCallback() override = default;
void OnConnectionAccepted(Ptr<WifiLanSocket> wifi_lan_socket,
absl::string_view service_id) override {
accepted_connection_callback_->OnConnectionAccepted(wifi_lan_socket,
service_id);
}
private:
ScopedPtr<Ptr<WifiLan::AcceptedConnectionCallback>>
accepted_connection_callback_;
};
struct DiscoveringInfo {
DiscoveringInfo() = default;
explicit DiscoveringInfo(absl::string_view service_id)
: service_id(service_id) {}
~DiscoveringInfo() = default;
string service_id;
};
struct AdvertisingInfo {
AdvertisingInfo() = default;
explicit AdvertisingInfo(absl::string_view service_id)
: service_id(service_id) {}
~AdvertisingInfo() = default;
string service_id;
};
struct AcceptingConnectionsInfo {
AcceptingConnectionsInfo() = default;
explicit AcceptingConnectionsInfo(absl::string_view service_id)
: service_id(service_id) {}
~AcceptingConnectionsInfo() = default;
string service_id;
};
// ------------ GENERAL ------------
ScopedPtr<Ptr<Lock>> lock_;
// ---------- CORE WIFILAN------------
// The underlying, per-platform implementation.
ScopedPtr<Ptr<WifiLanMedium>> wifi_lan_medium_;
// ------------ DISCOVERY ------------
// discovering_info_ is not scoped because it's nullable.
DiscoveringInfo discovering_info_;
// ------------ ADVERTISING ------------
// A bundle of state required to start/stop WifiLan service publishing.
AdvertisingInfo advertising_info_;
// A bundle of state required to start/stop accepting WifiLan service
/// connections.
AcceptingConnectionsInfo accepting_connections_info_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#include "core/internal/mediums/wifi_lan.cc"
#endif // CORE_INTERNAL_MEDIUMS_WIFI_LAN_H_
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MESSAGE_LITE_H_
#define CORE_INTERNAL_MESSAGE_LITE_H_
#include "google/protobuf/message_lite.h"
#endif // CORE_INTERNAL_MESSAGE_LITE_H_
+4 -1
View File
@@ -75,7 +75,8 @@ ExceptionOrOfflineFrame OfflineFrames::fromBytes(
ConstPtr<ByteArray> offline_frame_bytes) {
auto offline_frame = std::make_unique<OfflineFrame>();
if (!offline_frame->ParseFromString(offline_frame_bytes->asString())) {
if (!offline_frame->ParseFromArray(offline_frame_bytes->getData(),
offline_frame_bytes->size())) {
return ExceptionOrOfflineFrame(Exception::INVALID_PROTOCOL_BUFFER);
}
@@ -92,6 +93,7 @@ V1Frame::FrameType OfflineFrames::getFrameType(
return V1Frame::UNKNOWN_FRAME_TYPE;
}
// TODO(b/155752436): Use byte array endpoint_info instead of endpoint_name.
ConstPtr<ByteArray> OfflineFrames::forConnectionRequest(
const std::string &endpoint_id, const std::string &endpoint_name,
std::int32_t nonce,
@@ -99,6 +101,7 @@ ConstPtr<ByteArray> OfflineFrames::forConnectionRequest(
auto connection_request = std::make_unique<ConnectionRequestFrame>();
connection_request->set_endpoint_id(endpoint_id);
connection_request->set_endpoint_name(endpoint_name);
connection_request->set_endpoint_info(endpoint_name);
connection_request->set_nonce(nonce);
for (std::vector<proto::connections::Medium>::const_iterator it =
@@ -25,11 +25,11 @@ OfflineServiceController<Platform>::OfflineServiceController()
: ServiceController<Platform>(),
medium_manager_(new MediumManager<Platform>()),
endpoint_channel_manager_(
new EndpointChannelManager<Platform>(medium_manager_.get())),
new EndpointChannelManager(medium_manager_.get())),
endpoint_manager_(
new EndpointManager<Platform>(endpoint_channel_manager_.get())),
payload_manager_(new PayloadManager<Platform>(endpoint_manager_.get())),
bandwidth_upgrade_manager_(new BandwidthUpgradeManager<Platform>(
bandwidth_upgrade_manager_(new BandwidthUpgradeManager(
medium_manager_.get(), endpoint_channel_manager_.get(),
endpoint_manager_.get())),
pcp_manager_(new PCPManager<Platform>(
@@ -82,11 +82,10 @@ class OfflineServiceController : public ServiceController<Platform> {
// on the destructors running (strictly) in the reverse order; a deviation
// from that will lead to crashes at runtime.
ScopedPtr<Ptr<MediumManager<Platform> > > medium_manager_;
ScopedPtr<Ptr<EndpointChannelManager<Platform> > > endpoint_channel_manager_;
ScopedPtr<Ptr<EndpointChannelManager>> endpoint_channel_manager_;
ScopedPtr<Ptr<EndpointManager<Platform> > > endpoint_manager_;
ScopedPtr<Ptr<PayloadManager<Platform> > > payload_manager_;
ScopedPtr<Ptr<BandwidthUpgradeManager<Platform> > >
bandwidth_upgrade_manager_;
ScopedPtr<Ptr<BandwidthUpgradeManager>> bandwidth_upgrade_manager_;
ScopedPtr<Ptr<PCPManager<Platform> > > pcp_manager_;
};
+313 -3
View File
@@ -13,7 +13,6 @@
// limitations under the License.
#include "core/internal/p2p_cluster_pcp_handler.h"
#include "platform/api/hash_utils.h"
namespace location {
@@ -30,6 +29,11 @@ const BLEAdvertisement::Version::Value
P2PClusterPCPHandler<Platform>::kBleAdvertisementVersion =
BLEAdvertisement::Version::V1;
template <typename Platform>
const WifiLanServiceInfo::Version
P2PClusterPCPHandler<Platform>::kWifiLanServiceInfoVersion =
WifiLanServiceInfo::Version::kV1;
template <typename Platform>
ConstPtr<ByteArray> P2PClusterPCPHandler<Platform>::generateHash(
const string& source, size_t size) {
@@ -49,8 +53,8 @@ template <typename Platform>
P2PClusterPCPHandler<Platform>::P2PClusterPCPHandler(
Ptr<MediumManager<Platform>> medium_manager,
Ptr<EndpointManager<Platform>> endpoint_manager,
Ptr<EndpointChannelManager<Platform>> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform>> bandwidth_upgrade_manager)
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager)
: BasePCPHandler<Platform>(endpoint_manager, endpoint_channel_manager,
bandwidth_upgrade_manager),
medium_manager_(medium_manager) {}
@@ -72,6 +76,9 @@ template <typename Platform>
std::vector<proto::connections::Medium>
P2PClusterPCPHandler<Platform>::getConnectionMediumsByPriority() {
std::vector<proto::connections::Medium> mediums;
if (medium_manager_->IsWifiLanAvailable()) {
mediums.push_back(proto::connections::WIFI_LAN);
}
if (medium_manager_->isBluetoothAvailable()) {
mediums.push_back(proto::connections::BLUETOOTH);
}
@@ -95,6 +102,15 @@ P2PClusterPCPHandler<Platform>::startAdvertisingImpl(
const AdvertisingOptions& options) {
std::vector<proto::connections::Medium> mediums_started_successfully;
ScopedPtr<ConstPtr<ByteArray>> scoped_wifi_lan_service_id_hash(
generateHash(service_id, WifiLanServiceInfo::kServiceIdHashLength));
proto::connections::Medium wifi_lan_medium = StartWifiLanAdvertising(
client_proxy, service_id, scoped_wifi_lan_service_id_hash.get(),
local_endpoint_id, local_endpoint_name);
if (proto::connections::UNKNOWN_MEDIUM != wifi_lan_medium) {
mediums_started_successfully.push_back(wifi_lan_medium);
}
ScopedPtr<ConstPtr<ByteArray>> scoped_bluetooth_service_id_hash(
generateHash(service_id, BluetoothDeviceName::kServiceIdHashLength));
proto::connections::Medium bluetooth_medium = startBluetoothAdvertising(
@@ -132,10 +148,14 @@ Status::Value P2PClusterPCPHandler<Platform>::stopAdvertisingImpl(
Ptr<ClientProxy<Platform>> client_proxy) {
medium_manager_->stopBleAdvertising(client_proxy->getAdvertisingServiceId());
medium_manager_->turnOffBluetoothDiscoverability();
medium_manager_->StopWifiLanAdvertising(
client_proxy->getAdvertisingServiceId());
medium_manager_->stopListeningForIncomingBleConnections(
client_proxy->getAdvertisingServiceId());
medium_manager_->stopListeningForIncomingBluetoothConnections(
client_proxy->getAdvertisingServiceId());
medium_manager_->StopListeningForIncomingWifiLanConnections(
client_proxy->getAdvertisingServiceId());
return Status::SUCCESS;
}
@@ -146,6 +166,14 @@ P2PClusterPCPHandler<Platform>::startDiscoveryImpl(
const DiscoveryOptions& options) {
std::vector<proto::connections::Medium> mediums_started_successfully;
proto::connections::Medium wifi_lan_medium =
StartWifiLanDiscovery(MakePtr(new FoundWifiLanServiceProcessor(
self_, client_proxy, service_id)),
client_proxy, service_id);
if (proto::connections::UNKNOWN_MEDIUM != wifi_lan_medium) {
mediums_started_successfully.push_back(wifi_lan_medium);
}
proto::connections::Medium bluetooth_medium =
startBluetoothDiscovery(MakePtr(new FoundBluetoothAdvertisementProcessor(
self_, client_proxy, service_id)),
@@ -184,6 +212,12 @@ typename BasePCPHandler<Platform>::ConnectImplResult
P2PClusterPCPHandler<Platform>::connectImpl(
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<typename BasePCPHandler<Platform>::DiscoveredEndpoint> endpoint) {
Ptr<WifiLanEndpoint> wifi_lan_endpoint =
DowncastPtr<WifiLanEndpoint>(endpoint);
if (!wifi_lan_endpoint.isNull()) {
return WifiLanConnectImpl(client_proxy, wifi_lan_endpoint);
}
Ptr<BluetoothEndpoint> bluetooth_endpoint =
DowncastPtr<BluetoothEndpoint>(endpoint);
if (!bluetooth_endpoint.isNull()) {
@@ -309,6 +343,60 @@ void P2PClusterPCPHandler<Platform>::IncomingBleConnectionProcessor::
proto::connections::Medium::BLE);
}
//////////// P2PClusterPCPHandler::IncomingWifiLanConnectionProcessor /////////
template <typename Platform>
P2PClusterPCPHandler<Platform>::IncomingWifiLanConnectionProcessor::
IncomingWifiLanConnectionProcessor(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
absl::string_view local_endpoint_name)
: pcp_handler_(pcp_handler),
client_proxy_(client_proxy),
local_endpoint_name_(local_endpoint_name) {}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::IncomingWifiLanConnectionProcessor::
OnIncomingWifiLanConnection(Ptr<WifiLanSocket> wifi_lan_socket) {
pcp_handler_->runOnPCPHandlerThread(
MakePtr(new OnIncomingWifiLanConnectionRunnable(
pcp_handler_, client_proxy_, wifi_lan_socket)));
}
template <typename Platform>
P2PClusterPCPHandler<Platform>::IncomingWifiLanConnectionProcessor::
OnIncomingWifiLanConnectionRunnable::OnIncomingWifiLanConnectionRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<WifiLanSocket> wifi_lan_socket)
: pcp_handler_(pcp_handler),
client_proxy_(client_proxy),
wifi_lan_socket_(wifi_lan_socket) {}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::IncomingWifiLanConnectionProcessor::
OnIncomingWifiLanConnectionRunnable::run() {
string remote_service_name =
wifi_lan_socket_->GetRemoteWifiLanService()->GetName();
ScopedPtr<Ptr<EndpointChannel>> scoped_wifi_lan_endpoint_channel(
pcp_handler_->endpoint_channel_manager_
->CreateIncomingWifiLanEndpointChannel(remote_service_name,
wifi_lan_socket_));
if (!scoped_wifi_lan_endpoint_channel.isNull()) {
// TODO(b/149806065): Add logging.
} else {
Exception::Value exception = wifi_lan_socket_->Close();
wifi_lan_socket_.destroy();
if (Exception::NONE != exception) {
if (Exception::IO == exception) {
// TODO(b/149806065): Add logging.
}
}
}
pcp_handler_->onIncomingConnection(client_proxy_, remote_service_name,
scoped_wifi_lan_endpoint_channel.release(),
proto::connections::Medium::WIFI_LAN);
}
///////// P2PClusterPCPHandler::FoundBluetoothAdvertisementProcessor //////////
template <typename Platform>
P2PClusterPCPHandler<Platform>::FoundBluetoothAdvertisementProcessor::
@@ -596,6 +684,137 @@ void P2PClusterPCPHandler<Platform>::FoundBleAdvertisementProcessor::
}
}
////////// P2PClusterPCPHandler::FoundWifiLanServiceProcessor ///////////
template <typename Platform>
P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
FoundWifiLanServiceProcessor(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy, absl::string_view service_id)
: pcp_handler_(pcp_handler),
client_proxy_(client_proxy),
service_id_(service_id),
expected_service_id_hash_(generateHash(
string(service_id), WifiLanServiceInfo::kServiceIdHashLength)) {}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnFoundWifiLanService(Ptr<WifiLanService> wifi_lan_service) {
pcp_handler_->runOnPCPHandlerThread(MakePtr(new OnFoundWifiLanServiceRunnable(
pcp_handler_, client_proxy_, self_, service_id_, wifi_lan_service)));
}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnLostWifiLanService(Ptr<WifiLanService> wifi_lan_service) {
pcp_handler_->runOnPCPHandlerThread(MakePtr(new OnLostWifiLanServiceRunnable(
pcp_handler_, client_proxy_, self_, service_id_, wifi_lan_service)));
}
template <typename Platform>
bool P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
IsRecognizedWifiLanEndpoint(Ptr<WifiLanServiceInfo> wifi_lan_service_info) {
if (wifi_lan_service_info.isNull()) {
return false;
}
if (wifi_lan_service_info->GetPcp() != pcp_handler_->getPCP()) {
return false;
}
if (*(wifi_lan_service_info->GetServiceIdHash()) !=
*(expected_service_id_hash_.get())) {
return false;
}
return true;
}
template <typename Platform>
P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnFoundWifiLanServiceRunnable::OnFoundWifiLanServiceRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor,
absl::string_view service_id, Ptr<WifiLanService> wifi_lan_service)
: pcp_handler_(pcp_handler),
client_proxy_(client_proxy),
found_wifi_lan_service_processor_(found_wifi_lan_service_processor),
service_id_(service_id),
wifi_lan_service_(wifi_lan_service),
expected_service_id_hash_(generateHash(
string(service_id), WifiLanServiceInfo::kServiceIdHashLength)) {}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnFoundWifiLanServiceRunnable::run() {
// Make sure we are still discovering before proceeding.
if (!client_proxy_->isDiscovering()) {
return;
}
// Parse the WifiLan service name.
ScopedPtr<Ptr<WifiLanServiceInfo>> wifi_lan_service_info(
WifiLanServiceInfo::FromString(wifi_lan_service_->GetName()));
// Make sure the WifiLan service name points to a valid endpoint we're
// discovering.
if (!found_wifi_lan_service_processor_->IsRecognizedWifiLanEndpoint(
wifi_lan_service_info.get())) {
return;
}
// Report the discovered endpoint to the client.
pcp_handler_->onEndpointFound(
client_proxy_,
MakePtr(new WifiLanEndpoint(
wifi_lan_service_.release(),
wifi_lan_service_info->GetEndpointId(),
wifi_lan_service_info->GetEndpointName(), service_id_)));
}
template <typename Platform>
P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnLostWifiLanServiceRunnable::OnLostWifiLanServiceRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor,
absl::string_view service_id, Ptr<WifiLanService> wifi_lan_service)
: pcp_handler_(pcp_handler),
client_proxy_(client_proxy),
found_wifi_lan_service_processor_(found_wifi_lan_service_processor),
service_id_(service_id),
wifi_lan_service_(wifi_lan_service.operator->()) {}
template <typename Platform>
void P2PClusterPCPHandler<Platform>::FoundWifiLanServiceProcessor::
OnLostWifiLanServiceRunnable::run() {
// Make sure we are still discovering before proceeding.
if (!client_proxy_->isDiscovering()) {
// TODO(b/149806065): Add logging.
return;
}
// Parse the WifiLan service name.
ScopedPtr<Ptr<WifiLanServiceInfo>> wifi_lan_service_info(
WifiLanServiceInfo::FromString(wifi_lan_service_->GetName()));
// Make sure the WifiLan service name points to a valid endpoint we're
// discovering.
if (!found_wifi_lan_service_processor_->IsRecognizedWifiLanEndpoint(
wifi_lan_service_info.get())) {
return;
}
// Report the endpoint as lost to the client.
// TODO(b/149806065): Add logging.
pcp_handler_->onEndpointLost(
client_proxy_,
MakePtr(new WifiLanEndpoint(
Ptr<WifiLanService>(wifi_lan_service_.release()),
wifi_lan_service_info->GetEndpointId(),
wifi_lan_service_info->GetEndpointName(), service_id_)));
}
//////////////////// END IMPLEMENTATIONS FOR NESTED CLASSES ////////////////////
template <typename Platform>
@@ -724,6 +943,65 @@ proto::connections::Medium P2PClusterPCPHandler<Platform>::startBleDiscovery(
return proto::connections::BLE;
}
template <typename Platform>
proto::connections::Medium
P2PClusterPCPHandler<Platform>::StartWifiLanAdvertising(
Ptr<ClientProxy<Platform>> client_proxy, absl::string_view service_id,
ConstPtr<ByteArray> service_id_hash, absl::string_view local_endpoint_id,
absl::string_view local_endpoint_name) {
// Start listening for connections before advertising in case a connection
// request comes in very quickly.
if (!medium_manager_->IsListeningForIncomingWifiLanConnections(service_id)) {
if (!medium_manager_->StartListeningForIncomingWifiLanConnections(
service_id, MakePtr(new IncomingWifiLanConnectionProcessor(
self_, client_proxy, local_endpoint_name)))) {
// TODO(b/149806065): logger.atWarning().log("In
// StartWifiLanAdvertising(%s), client %d failed to start listening for
// incoming WifiLan connections to ServiceId %s", local_endpoint_name,
// clientProxy.getClientId(), service_id);
return proto::connections::UNKNOWN_MEDIUM;
}
// TODO(b/149806065): Add logging.
}
// Generate a WifiLanServiceInfo.
const string wifi_lan_service_info =
WifiLanServiceInfo::AsString(kWifiLanServiceInfoVersion,
getPCP(),
local_endpoint_id,
service_id_hash);
if (wifi_lan_service_info.empty()) {
// TODO(b/149806065): Add logging.
return proto::connections::UNKNOWN_MEDIUM;
} else {
// TODO(b/149806065): Add logging.
}
// TODO(b/149806065): Add logging
if (!medium_manager_->StartWifiLanAdvertising(
service_id, wifi_lan_service_info)) {
// TODO(b/149806065): Add logging
medium_manager_->StopWifiLanAdvertising(service_id);
return proto::connections::UNKNOWN_MEDIUM;
}
return proto::connections::WIFI_LAN;
}
template <typename Platform>
proto::connections::Medium
P2PClusterPCPHandler<Platform>::StartWifiLanDiscovery(
Ptr<FoundWifiLanServiceProcessor> processor,
Ptr<ClientProxy<Platform> > client_proxy, absl::string_view service_id) {
if (!medium_manager_->StartWifiLanDiscovery(service_id, processor)) {
// TODO(b/149806065): Add logging.
return proto::connections::UNKNOWN_MEDIUM;
}
return proto::connections::WIFI_LAN;
}
template <typename Platform>
typename BasePCPHandler<Platform>::ConnectImplResult
P2PClusterPCPHandler<Platform>::bluetoothConnectImpl(
@@ -797,6 +1075,38 @@ string P2PClusterPCPHandler<Platform>::getBlePeripheralId(
#endif
}
template <typename Platform>
typename BasePCPHandler<Platform>::ConnectImplResult
P2PClusterPCPHandler<Platform>::WifiLanConnectImpl(
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<WifiLanEndpoint> wifi_lan_endpoint) {
Ptr<WifiLanService> remote_wifi_lan_service =
wifi_lan_endpoint->GetWifiLanService();
Ptr<WifiLanSocket> wifi_lan_socket = medium_manager_->ConnectToWifiLanService(
remote_wifi_lan_service, wifi_lan_endpoint->getServiceId());
if (wifi_lan_socket.isNull()) {
return typename BasePCPHandler<Platform>::ConnectImplResult(
proto::connections::Medium::WIFI_LAN, Status::BLUETOOTH_ERROR);
}
ScopedPtr<Ptr<EndpointChannel>> scoped_wifi_lan_endpoint_channel(
this->endpoint_channel_manager_->CreateOutgoingWifiLanEndpointChannel(
wifi_lan_endpoint->getEndpointId(), wifi_lan_socket));
if (scoped_wifi_lan_endpoint_channel.isNull()) {
wifi_lan_socket->Close();
wifi_lan_socket.destroy(); // Avoid leaks.
return typename BasePCPHandler<Platform>::ConnectImplResult(
proto::connections::Medium::WIFI_LAN, Status::ERROR);
}
// TODO(b/149806065): Add logging.
return typename BasePCPHandler<Platform>::ConnectImplResult(
scoped_wifi_lan_endpoint_channel.release());
}
} // namespace connections
} // namespace nearby
} // namespace location
+204 -66
View File
@@ -27,6 +27,7 @@
#include "core/internal/endpoint_manager.h"
#include "core/internal/medium_manager.h"
#include "core/internal/pcp.h"
#include "core/internal/wifi_lan_service_info.h"
#include "core/options.h"
#include "core/strategy.h"
#include "platform/api/bluetooth_classic.h"
@@ -49,11 +50,10 @@ namespace connections {
template <typename Platform>
class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
public:
P2PClusterPCPHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager);
P2PClusterPCPHandler(Ptr<MediumManager<Platform>> medium_manager,
Ptr<EndpointManager<Platform>> endpoint_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
~P2PClusterPCPHandler() override;
Strategy getStrategy() override;
@@ -66,27 +66,29 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
// @PCPHandlerThread
Ptr<typename BasePCPHandler<Platform>::StartOperationResult>
startAdvertisingImpl(Ptr<ClientProxy<Platform> > client_proxy,
startAdvertisingImpl(Ptr<ClientProxy<Platform>> client_proxy,
const string& service_id,
const string& local_endpoint_id,
const string& local_endpoint_name,
const AdvertisingOptions& options) override;
// @PCPHandlerThread
Status::Value stopAdvertisingImpl(
Ptr<ClientProxy<Platform> > client_proxy) override;
Ptr<ClientProxy<Platform>> client_proxy) override;
// @PCPHandlerThread
Ptr<typename BasePCPHandler<Platform>::StartOperationResult>
startDiscoveryImpl(Ptr<ClientProxy<Platform> > client_proxy,
startDiscoveryImpl(Ptr<ClientProxy<Platform>> client_proxy,
const string& service_id,
const DiscoveryOptions& options) override;
// @PCPHandlerThread
Status::Value stopDiscoveryImpl(
Ptr<ClientProxy<Platform> > client_proxy) override;
Ptr<ClientProxy<Platform>> client_proxy) override;
// @PCPHandlerThread
typename BasePCPHandler<Platform>::ConnectImplResult connectImpl(
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<typename BasePCPHandler<Platform>::DiscoveredEndpoint> endpoint)
override;
@@ -96,16 +98,20 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
template <typename>
friend class IncomingBleConnectionProcessor;
template <typename>
friend class IncomingWifiLanConnectionProcessor;
template <typename>
friend class FoundBluetoothAdvertisementProcessor;
template <typename>
friend class FoundBleAdvertisementProcessor;
template <typename>
friend class FoundWifiLanServiceProcessor;
class IncomingBluetoothConnectionProcessor
: public MediumManager<Platform>::IncomingBluetoothConnectionProcessor {
public:
IncomingBluetoothConnectionProcessor(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
const string& local_endpoint_name);
void onIncomingBluetoothConnection(
@@ -115,20 +121,20 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
class OnIncomingBluetoothConnectionRunnable : public Runnable {
public:
OnIncomingBluetoothConnectionRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<BluetoothSocket> bluetooth_socket);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<BluetoothSocket> bluetooth_socket_;
};
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
const string local_endpoint_name_;
};
@@ -136,8 +142,8 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
: public MediumManager<Platform>::IncomingBleConnectionProcessor {
public:
IncomingBleConnectionProcessor(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
const string& local_endpoint_name);
void onIncomingBleConnection(Ptr<BLESocket> ble_socket,
@@ -147,19 +153,51 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
class OnIncomingBleConnectionRunnable : public Runnable {
public:
OnIncomingBleConnectionRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy, Ptr<BLESocket> ble_socket);
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy, Ptr<BLESocket> ble_socket);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<BLESocket> ble_socket_;
};
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
const string local_endpoint_name_;
};
class IncomingWifiLanConnectionProcessor
: public MediumManager<Platform>::IncomingWifiLanConnectionProcessor {
public:
IncomingWifiLanConnectionProcessor(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
absl::string_view local_endpoint_name);
void OnIncomingWifiLanConnection(
Ptr<WifiLanSocket> wifi_lan_socket) override;
private:
class OnIncomingWifiLanConnectionRunnable : public Runnable {
public:
OnIncomingWifiLanConnectionRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<WifiLanSocket> wifi_lan_socket);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<WifiLanSocket> wifi_lan_socket_;
};
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
const string local_endpoint_name_;
};
@@ -167,8 +205,8 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
: public MediumManager<Platform>::FoundBluetoothDeviceProcessor {
public:
FoundBluetoothAdvertisementProcessor(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id);
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy, const string& service_id);
void onFoundBluetoothDevice(Ptr<BluetoothDevice> bluetooth_device) override;
void onLostBluetoothDevice(Ptr<BluetoothDevice> bluetooth_device) override;
@@ -177,8 +215,8 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
class OnFoundBluetoothDeviceRunnable : public Runnable {
public:
OnFoundBluetoothDeviceRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundBluetoothAdvertisementProcessor>
found_bluetooth_advertisement_processor,
const string& service_id, Ptr<BluetoothDevice> bluetooth_device);
@@ -186,19 +224,19 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundBluetoothAdvertisementProcessor>
found_bluetooth_advertisement_processor_;
const string service_id_;
ScopedPtr<Ptr<BluetoothDevice> > bluetooth_device_;
ScopedPtr<Ptr<BluetoothDevice>> bluetooth_device_;
};
class OnLostBluetoothDeviceRunnable : public Runnable {
public:
OnLostBluetoothDeviceRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundBluetoothAdvertisementProcessor>
found_bluetooth_advertisement_processor,
const string& service_id, Ptr<BluetoothDevice> bluetooth_device);
@@ -206,22 +244,22 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundBluetoothAdvertisementProcessor>
found_bluetooth_advertisement_processor_;
const string service_id_;
ScopedPtr<Ptr<BluetoothDevice> > bluetooth_device_;
ScopedPtr<Ptr<BluetoothDevice>> bluetooth_device_;
};
bool isRecognizedBluetoothEndpoint(
const string& found_bluetooth_device_name,
Ptr<BluetoothDeviceName> bluetooth_device_name);
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
const string service_id_;
ScopedPtr<ConstPtr<ByteArray> > expected_service_id_hash_;
ScopedPtr<ConstPtr<ByteArray>> expected_service_id_hash_;
std::shared_ptr<FoundBluetoothAdvertisementProcessor> self_{this,
[](void*) {}};
};
@@ -230,8 +268,8 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
: public MediumManager<Platform>::FoundBlePeripheralProcessor {
public:
FoundBleAdvertisementProcessor(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy);
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy);
void onFoundBlePeripheral(Ptr<BLE_PERIPHERAL> ble_peripheral,
const string& service_id,
@@ -243,8 +281,8 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
class OnFoundBlePeripheralRunnable : public Runnable {
public:
OnFoundBlePeripheralRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundBleAdvertisementProcessor> found_ble_advertisement_processor,
const string& service_id, Ptr<BLE_PERIPHERAL> ble_peripheral,
ConstPtr<ByteArray> advertisement_bytes);
@@ -252,31 +290,31 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundBleAdvertisementProcessor> found_ble_advertisement_processor_;
const string service_id_;
ScopedPtr<Ptr<BLE_PERIPHERAL> > ble_peripheral_;
ScopedPtr<ConstPtr<ByteArray> > advertisement_bytes_;
ScopedPtr<ConstPtr<ByteArray> > expected_service_id_hash_;
ScopedPtr<Ptr<BLE_PERIPHERAL>> ble_peripheral_;
ScopedPtr<ConstPtr<ByteArray>> advertisement_bytes_;
ScopedPtr<ConstPtr<ByteArray>> expected_service_id_hash_;
};
class OnLostBlePeripheralRunnable : public Runnable {
public:
OnLostBlePeripheralRunnable(
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler,
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundBleAdvertisementProcessor> found_ble_advertisement_processor,
const string& service_id, Ptr<BLE_PERIPHERAL> ble_peripheral);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundBleAdvertisementProcessor> found_ble_advertisement_processor_;
const string service_id_;
ScopedPtr<Ptr<BLE_PERIPHERAL> > ble_peripheral_;
ScopedPtr<Ptr<BLE_PERIPHERAL>> ble_peripheral_;
};
// Holds the state required to re-create a BLEEndpoint we see on a
@@ -292,14 +330,74 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
const string endpoint_name;
};
Ptr<P2PClusterPCPHandler<Platform> > pcp_handler_;
Ptr<ClientProxy<Platform> > client_proxy_;
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
// Maps a BLEPeripheral to its corresponding BLEEndpointState.
typedef std::map<string, BLEEndpointState> FoundBLEEndpointsMap;
FoundBLEEndpointsMap found_ble_endpoints_;
std::shared_ptr<FoundBleAdvertisementProcessor> self_{this, [](void*) {}};
};
class FoundWifiLanServiceProcessor
: public MediumManager<Platform>::FoundWifiLanServiceProcessor {
public:
FoundWifiLanServiceProcessor(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
absl::string_view service_id);
void OnFoundWifiLanService(Ptr<WifiLanService> wifi_lan_service) override;
void OnLostWifiLanService(Ptr<WifiLanService> wifi_lan_service) override;
private:
class OnFoundWifiLanServiceRunnable : public Runnable {
public:
OnFoundWifiLanServiceRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor,
absl::string_view service_id, Ptr<WifiLanService> wifi_lan_service);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor_;
const string service_id_;
ScopedPtr<Ptr<WifiLanService>> wifi_lan_service_;
ScopedPtr<ConstPtr<ByteArray>> expected_service_id_hash_;
};
class OnLostWifiLanServiceRunnable : public Runnable {
public:
OnLostWifiLanServiceRunnable(
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor,
absl::string_view service_id, Ptr<WifiLanService> wifi_lan_service);
void run() override;
private:
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<FoundWifiLanServiceProcessor> found_wifi_lan_service_processor_;
const string service_id_;
ScopedPtr<Ptr<WifiLanService>> wifi_lan_service_;
};
bool IsRecognizedWifiLanEndpoint(
Ptr<WifiLanServiceInfo> wifi_lan_service_info);
Ptr<P2PClusterPCPHandler<Platform>> pcp_handler_;
Ptr<ClientProxy<Platform>> client_proxy_;
const string service_id_;
ScopedPtr<ConstPtr<ByteArray>> expected_service_id_hash_;
std::shared_ptr<FoundWifiLanServiceProcessor> self_{this, [](void*) {}};
};
class BluetoothEndpoint
: public BasePCPHandler<Platform>::DiscoveredEndpoint {
public:
@@ -324,7 +422,7 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
friend class FoundBluetoothAdvertisementProcessor;
ScopedPtr<Ptr<BluetoothDevice> > bluetooth_device_;
ScopedPtr<Ptr<BluetoothDevice>> bluetooth_device_;
const string endpoint_id_;
const string endpoint_name_;
const string service_id_;
@@ -350,7 +448,35 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
friend class FoundBleAdvertisementProcessor;
ScopedPtr<Ptr<BLE_PERIPHERAL> > ble_peripheral_;
ScopedPtr<Ptr<BLE_PERIPHERAL>> ble_peripheral_;
const string endpoint_id_;
const string endpoint_name_;
const string service_id_;
};
class WifiLanEndpoint : public BasePCPHandler<Platform>::DiscoveredEndpoint {
public:
Ptr<WifiLanService> GetWifiLanService() { return wifi_lan_service_.get(); }
string getEndpointId() override { return endpoint_id_; }
string getEndpointName() override { return endpoint_name_; }
string getServiceId() override { return service_id_; }
proto::connections::Medium getMedium() override {
return proto::connections::Medium::WIFI_LAN;
}
private:
WifiLanEndpoint(Ptr<WifiLanService> wifi_lan_service,
absl::string_view endpoint_id,
absl::string_view endpoint_name,
absl::string_view service_id)
: wifi_lan_service_(wifi_lan_service),
endpoint_id_(endpoint_id),
endpoint_name_(endpoint_name),
service_id_(service_id) {}
friend class FoundWifiLanServiceProcessor;
ScopedPtr<Ptr<WifiLanService>> wifi_lan_service_;
const string endpoint_id_;
const string endpoint_name_;
const string service_id_;
@@ -358,32 +484,44 @@ class P2PClusterPCPHandler : public BasePCPHandler<Platform> {
static const BluetoothDeviceName::Version::Value kBluetoothDeviceNameVersion;
static const BLEAdvertisement::Version::Value kBleAdvertisementVersion;
static const WifiLanServiceInfo::Version kWifiLanServiceInfoVersion;
static ConstPtr<ByteArray> generateHash(const string& source, size_t size);
static string getBlePeripheralId(Ptr<BLE_PERIPHERAL> ble_peripheral);
proto::connections::Medium startBluetoothAdvertising(
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
Ptr<ClientProxy<Platform>> client_proxy, const string& service_id,
ConstPtr<ByteArray> service_id_hash, const string& local_endpoint_id,
const string& local_endpoint_name);
proto::connections::Medium startBluetoothDiscovery(
Ptr<FoundBluetoothAdvertisementProcessor> processor,
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id);
Ptr<ClientProxy<Platform>> client_proxy, const string& service_id);
typename BasePCPHandler<Platform>::ConnectImplResult bluetoothConnectImpl(
Ptr<ClientProxy<Platform> > client_proxy,
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<BluetoothEndpoint> bluetooth_endpoint);
proto::connections::Medium startBleAdvertising(
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id,
Ptr<ClientProxy<Platform>> client_proxy, const string& service_id,
ConstPtr<ByteArray> service_id_hash, const string& local_endpoint_id,
const string& local_endpoint_name);
proto::connections::Medium startBleDiscovery(
Ptr<FoundBleAdvertisementProcessor> processor,
Ptr<ClientProxy<Platform> > client_proxy, const string& service_id);
Ptr<ClientProxy<Platform>> client_proxy, const string& service_id);
typename BasePCPHandler<Platform>::ConnectImplResult bleConnectImpl(
Ptr<ClientProxy<Platform> > client_proxy, Ptr<BLEEndpoint> ble_endpoint);
Ptr<ClientProxy<Platform>> client_proxy, Ptr<BLEEndpoint> ble_endpoint);
Ptr<MediumManager<Platform> > medium_manager_;
proto::connections::Medium StartWifiLanAdvertising(
Ptr<ClientProxy<Platform>> client_proxy, absl::string_view service_id,
ConstPtr<ByteArray> service_id_hash, absl::string_view local_endpoint_id,
absl::string_view local_endpoint_name);
proto::connections::Medium StartWifiLanDiscovery(
Ptr<FoundWifiLanServiceProcessor> processor,
Ptr<ClientProxy<Platform>> client_proxy, absl::string_view service_id);
typename BasePCPHandler<Platform>::ConnectImplResult WifiLanConnectImpl(
Ptr<ClientProxy<Platform>> client_proxy,
Ptr<WifiLanEndpoint> wifi_lan_endpoint);
Ptr<MediumManager<Platform>> medium_manager_;
std::shared_ptr<P2PClusterPCPHandler> self_{this, [](void*) {}};
};
@@ -22,8 +22,8 @@ template <typename Platform>
P2PPointToPointPCPHandler<Platform>::P2PPointToPointPCPHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager)
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager)
: P2PStarPCPHandler<Platform>(medium_manager, endpoint_manager,
endpoint_channel_manager,
bandwidth_upgrade_manager),
@@ -41,8 +41,8 @@ class P2PPointToPointPCPHandler : public P2PStarPCPHandler<Platform> {
P2PPointToPointPCPHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager);
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
Strategy getStrategy() override;
PCP::Value getPCP() override;
+2 -2
View File
@@ -24,8 +24,8 @@ template <typename Platform>
P2PStarPCPHandler<Platform>::P2PStarPCPHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager)
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager)
: P2PClusterPCPHandler<Platform>(medium_manager, endpoint_manager,
endpoint_channel_manager,
bandwidth_upgrade_manager),
+4 -5
View File
@@ -41,11 +41,10 @@ namespace connections {
template <typename Platform>
class P2PStarPCPHandler : public P2PClusterPCPHandler<Platform> {
public:
P2PStarPCPHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager);
P2PStarPCPHandler(Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
~P2PStarPCPHandler() override;
Strategy getStrategy() override;
+2 -2
View File
@@ -25,9 +25,9 @@ namespace connections {
template <typename Platform>
PCPManager<Platform>::PCPManager(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager)
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager)
: pcp_handlers_(), current_pcp_handler_() {
pcp_handlers_[PCP::P2P_CLUSTER] = MakePtr(new P2PClusterPCPHandler<Platform>(
medium_manager, endpoint_manager, endpoint_channel_manager,
+2 -2
View File
@@ -43,9 +43,9 @@ template <typename Platform>
class PCPManager {
public:
PCPManager(Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager,
Ptr<EndpointChannelManager> endpoint_channel_manager,
Ptr<EndpointManager<Platform> > endpoint_manager,
Ptr<BandwidthUpgradeManager<Platform> > bandwidth_upgrade_manager);
Ptr<BandwidthUpgradeManager> bandwidth_upgrade_manager);
~PCPManager();
Status::Value startAdvertising(
@@ -0,0 +1,63 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core/internal/wifi_lan_endpoint_channel.h"
#include <string>
namespace location {
namespace nearby {
namespace connections {
Ptr<WifiLanEndpointChannel>
WifiLanEndpointChannel::CreateOutgoing(
Ptr<MediumManager<Platform>> medium_manager,
absl::string_view channel_name, Ptr<WifiLanSocket> wifi_lan_socket) {
return MakePtr(
new WifiLanEndpointChannel(channel_name, wifi_lan_socket));
}
Ptr<WifiLanEndpointChannel>
WifiLanEndpointChannel::CreateIncoming(
Ptr<MediumManager<Platform>> medium_manager,
absl::string_view channel_name, Ptr<WifiLanSocket> wifi_lan_socket) {
return MakePtr(
new WifiLanEndpointChannel(channel_name, wifi_lan_socket));
}
WifiLanEndpointChannel::WifiLanEndpointChannel(
absl::string_view channel_name, Ptr<WifiLanSocket> wifi_lan_socket)
: BaseEndpointChannel(channel_name,
wifi_lan_socket->GetInputStream(),
wifi_lan_socket->GetOutputStream()),
wifi_lan_socket_(wifi_lan_socket) {}
WifiLanEndpointChannel::~WifiLanEndpointChannel() {}
proto::connections::Medium WifiLanEndpointChannel::getMedium() {
return proto::connections::Medium::WIFI_LAN;
}
void WifiLanEndpointChannel::closeImpl() {
Exception::Value exception = wifi_lan_socket_->Close();
if (exception != Exception::NONE) {
if (exception == Exception::IO) {
// TODO(b/149806065): Add logging.
}
}
}
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,60 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_
#define CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_
#include "core/internal/base_endpoint_channel.h"
#include "core/internal/medium_manager.h"
#include "platform/api/platform.h"
#include "platform/api/wifi_lan.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
#include "proto/connections_enums.pb.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace connections {
class WifiLanEndpointChannel : public BaseEndpointChannel {
public:
using Platform = platform::ImplementationPlatform;
static Ptr<WifiLanEndpointChannel> CreateOutgoing(
Ptr<MediumManager<Platform>> medium_manager,
absl::string_view channel_name, Ptr<WifiLanSocket> wifi_lan_socket);
static Ptr<WifiLanEndpointChannel> CreateIncoming(
Ptr<MediumManager<Platform>> medium_manager,
absl::string_view channel_name, Ptr<WifiLanSocket> wifi_lan_socket);
~WifiLanEndpointChannel() override;
proto::connections::Medium getMedium() override;
protected:
void closeImpl() override;
private:
WifiLanEndpointChannel(absl::string_view channel_name,
Ptr<WifiLanSocket> wifi_lan_socket);
ScopedPtr<Ptr<WifiLanSocket> > wifi_lan_socket_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_
@@ -32,8 +32,8 @@ class OnIncomingWifiConnectionRunnable : public Runnable {
template <typename Platform>
WifiLanUpgradeHandler<Platform>::WifiLanUpgradeHandler(
Ptr<MediumManager<Platform> > medium_manager,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager)
: BaseBandwidthUpgradeHandler<Platform>(endpoint_channel_manager),
Ptr<EndpointChannelManager> endpoint_channel_manager)
: BaseBandwidthUpgradeHandler(endpoint_channel_manager),
medium_manager_(medium_manager) {}
template <typename Platform>
+13 -14
View File
@@ -37,36 +37,35 @@ class OnIncomingWifiConnectionRunnable;
// Manages the WIFI_LAN-specific methods needed to upgrade an EndpointChannel
template <typename Platform>
class WifiLanUpgradeHandler : public BaseBandwidthUpgradeHandler<Platform> {
class WifiLanUpgradeHandler : public BaseBandwidthUpgradeHandler {
// TODO(ahlee): Uncomment when WIFI_LAN plumbing is done.
// public MediumManager<Platform>::IncomingWifiConnectionProcessor {
public:
WifiLanUpgradeHandler(
Ptr<MediumManager<Platform> > medium_manager_,
Ptr<EndpointChannelManager<Platform> > endpoint_channel_manager);
~WifiLanUpgradeHandler();
WifiLanUpgradeHandler(Ptr<MediumManager<Platform> > medium_manager_,
Ptr<EndpointChannelManager> endpoint_channel_manager);
~WifiLanUpgradeHandler() override;
void onIncomingWifiConnection(Ptr<Socket> socket);
protected:
// @BandwidthUpgradeHandlerThread
ConstPtr<ByteArray> initializeUpgradedMediumForEndpoint(
const string& endpoint_id);
const string& endpoint_id) override;
// @BandwidthUpgradeHandlerThread
Ptr<EndpointChannel> createUpgradedEndpointChannel(
const string& endpoint_id,
ConstPtr<BandwidthUpgradeNegotiationFrame::UpgradePathInfo>
upgrade_path_info);
upgrade_path_info) override;
// TODO(ahlee): Change the java counterparts of these methods to private.
proto::connections::Medium getUpgradeMedium();
proto::connections::Medium getUpgradeMedium() override;
// @BandwidthUpgradeHandlerThread
void revertImpl();
void revertImpl() override;
private:
class IncomingWifiLanSocketConnection
: public BaseBandwidthUpgradeHandler<Platform>::IncomingSocketConnection {
: public BaseBandwidthUpgradeHandler::IncomingSocketConnection {
public:
IncomingWifiLanSocketConnection(Ptr<Socket> socket)
explicit IncomingWifiLanSocketConnection(Ptr<Socket> socket)
: new_endpoint_channel_(Ptr<EndpointChannel>()),
// TODO(ahlee): Uncomment when plumbing for WIFI_LAN is done.
// new_endpoint_channel_(getEndpointChannelManager()
@@ -75,15 +74,15 @@ class WifiLanUpgradeHandler : public BaseBandwidthUpgradeHandler<Platform> {
// TODO(ahlee): This is only used for logging which is not currently
// implemented. If we want to match the Java code in the future, we'll need
// to add toString() to socket.h.
string socketToString() { return string(); }
void closeSocket() {
string socketToString() override { return string(); }
void closeSocket() override {
// Ignore the potential Exception returned by close(), as a counterpart
// to Java's closeQuietly().
wifi_socket_->close();
}
// TODO(ahlee): Double check that the ownership of this is correct when
// this is fully implemented.
Ptr<EndpointChannel> getEndpointChannel() {
Ptr<EndpointChannel> getEndpointChannel() override {
return new_endpoint_channel_.release();
}
+87
View File
@@ -0,0 +1,87 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cc_library(
name = "core_v2",
srcs = [
"core.cc",
],
hdrs = [
"core.h",
],
visibility = [
"//core_v2:__subpackages__",
],
deps = [
":core_types",
"//core_v2/internal",
"//platform_v2/public",
"//platform_v2/public:logging",
"//absl/strings",
"//absl/time",
"//absl/types:span",
],
)
cc_library(
name = "core_types",
srcs = [
"strategy.cc",
],
hdrs = [
"listeners.h",
"options.h",
"params.h",
"payload.h",
"status.h",
"strategy.h",
],
visibility = [
"//core_v2:__subpackages__",
],
deps = [
"//platform_v2/base",
"//platform_v2/public",
"//platform_v2/public:logging",
"//absl/strings",
"//absl/types:variant",
],
)
cc_test(
name = "core_v2_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",
":core_v2",
"//core_v2/internal",
"//core_v2/internal:internal_test",
"//platform_v2/base",
"//platform_v2/impl/g3",
"//platform_v2/public",
"//platform_v2/public:logging",
"//testing/base/public:gunit_main",
"//absl/strings",
"//absl/time",
"//absl/types:variant",
],
)
+121
View File
@@ -0,0 +1,121 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/core.h"
#include <cassert>
#include <vector>
#include "core_v2/options.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/logging.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
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");
}
}
void Core::StartAdvertising(absl::string_view service_id,
ConnectionOptions options,
ConnectionRequestInfo info,
ResultCallback callback) {
assert(!service_id.empty());
assert(options.strategy.IsValid());
router_.StartAdvertising(&client_, service_id, options, info, callback);
}
void Core::StopAdvertising(const ResultCallback callback) {
router_.StopAdvertising(&client_, callback);
}
void Core::StartDiscovery(absl::string_view service_id,
ConnectionOptions options, DiscoveryListener listener,
ResultCallback callback) {
assert(!service_id.empty());
assert(options.strategy.IsValid());
router_.StartDiscovery(&client_, service_id, options, listener, callback);
}
void Core::StopDiscovery(ResultCallback callback) {
router_.StopDiscovery(&client_, callback);
}
void Core::RequestConnection(absl::string_view endpoint_id,
ConnectionRequestInfo info,
ResultCallback callback) {
assert(!endpoint_id.empty());
router_.RequestConnection(&client_, endpoint_id, info, callback);
}
void Core::AcceptConnection(absl::string_view endpoint_id,
PayloadListener listener, ResultCallback callback) {
assert(!endpoint_id.empty());
router_.AcceptConnection(&client_, endpoint_id, listener, callback);
}
void Core::RejectConnection(absl::string_view endpoint_id,
ResultCallback callback) {
assert(!endpoint_id.empty());
router_.RejectConnection(&client_, endpoint_id, callback);
}
void Core::InitiateBandwidthUpgrade(absl::string_view endpoint_id,
ResultCallback callback) {
router_.InitiateBandwidthUpgrade(&client_, endpoint_id, callback);
}
void Core::SendPayload(absl::Span<const std::string> endpoint_ids,
Payload payload, ResultCallback callback) {
assert(payload.GetType() != Payload::Type::kUnknown);
assert(!endpoint_ids.empty());
router_.SendPayload(&client_, endpoint_ids, std::move(payload), callback);
}
void Core::CancelPayload(std::int64_t payload_id, ResultCallback callback) {
assert(payload_id != 0);
router_.CancelPayload(&client_, payload_id, callback);
}
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
} // namespace nearby
} // namespace location
+222
View File
@@ -0,0 +1,222 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_CORE_H_
#define CORE_V2_CORE_H_
#include <string>
#include "core_v2/internal/client_proxy.h"
#include "core_v2/internal/service_controller.h"
#include "core_v2/internal/service_controller_router.h"
#include "core_v2/listeners.h"
#include "core_v2/options.h"
#include "core_v2/params.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.
class Core {
public:
explicit Core(std::function<ServiceController*()> factory)
: router_(factory) {}
~Core();
Core(Core&&) = default;
Core& operator=(Core&&) = default;
// 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);
// 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, 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:
static constexpr absl::Duration kWaitForDisconnect = absl::Milliseconds(5000);
ClientProxy client_;
ServiceControllerRouter router_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_CORE_H_
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
TEST(CoreTest, ConstructorDestructorWorks) {
MockServiceController mock;
Core core{[&mock]() { return &mock; }};
}
TEST(CoreTest, DestructorReportsFatalFailure) {
MockServiceController mock;
ON_CALL(mock, StopDiscovery).WillByDefault([](ClientProxy* client) {
NEARBY_LOG(INFO, "Blocking Endpoint disconnect for 10 sec");
absl::SleepFor(absl::Milliseconds(10000));
});
ASSERT_DEATH(
[&mock]() {
Core core{[&mock]() { return &mock; }};
EXPECT_CALL(mock, StartDiscovery).Times(1);
EXPECT_CALL(mock, StopAdvertising).Times(1);
core.StartDiscovery("service_id", {.strategy = Strategy::kP2pCluster},
{}, {.result_cb = [](Status status) {
NEARBY_LOG(INFO, "Discovery status: %d",
static_cast<int>(status.value));
}});
}(),
"Unable to shutdown");
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+115
View File
@@ -0,0 +1,115 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cc_library(
name = "internal",
srcs = [
"base_endpoint_channel.cc",
"base_pcp_handler.cc",
"ble_advertisement.cc",
"client_proxy.cc",
"encryption_runner.cc",
"endpoint_channel_manager.cc",
"endpoint_manager.cc",
"offline_frames.cc",
"service_controller_router.cc",
"wifi_lan_service_info.cc",
],
hdrs = [
"base_endpoint_channel.h",
"base_pcp_handler.h",
"ble_advertisement.h",
"client_proxy.h",
"encryption_runner.h",
"endpoint_channel.h",
"endpoint_channel_manager.h",
"endpoint_manager.h",
"offline_frames.h",
"pcp.h",
"pcp_handler.h",
"service_controller.h",
"service_controller_router.h",
"wifi_lan_service_info.h",
],
visibility = [
"//core_v2:__pkg__",
],
deps = [
"//core/internal:message_lite",
"//core_v2:core_types",
"//proto/connections:offline_wire_formats_portable_proto",
"//platform_v2/base",
"//platform_v2/public",
"//platform_v2/public:logging",
"//proto:connections_enums_portable_proto",
"//securegcm:ukey2",
"//absl/base:core_headers",
"//absl/container:flat_hash_map",
"//absl/container:flat_hash_set",
"//absl/strings",
"//absl/time",
"//absl/types:span",
],
)
cc_library(
name = "internal_test",
testonly = True,
hdrs = [
"mock_service_controller.h",
],
visibility = [
"//core_v2:__subpackages__",
],
deps = [
":internal",
"//testing/base/public:gunit",
],
)
cc_test(
name = "core_v2_internal_test",
size = "small",
srcs = [
"base_endpoint_channel_test.cc",
"base_pcp_handler_test.cc",
"ble_advertisement_test.cc",
"client_proxy_test.cc",
"encryption_runner_test.cc",
"endpoint_channel_manager_test.cc",
"endpoint_manager_test.cc",
"offline_frames_test.cc",
"service_controller_router_test.cc",
"wifi_lan_service_info_test.cc",
],
shard_count = 16,
deps = [
":internal",
":internal_test",
"//core_v2:core_types",
"//proto/connections:offline_wire_formats_portable_proto",
"//platform_v2/base",
"//platform_v2/impl/g3", # build_cleaner: keep
"//platform_v2/public",
"//platform_v2/public:logging",
"//proto:connections_enums_portable_proto",
"//securegcm:ukey2",
"//testing/base/public:gunit",
"//testing/base/public:gunit_main",
"//absl/container:flat_hash_set",
"//absl/synchronization",
"//absl/time",
"//absl/types:span",
],
)
@@ -0,0 +1,284 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/base_endpoint_channel.h"
#include <cassert>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/mutex_lock.h"
#include "proto/connections_enums.pb.h"
#include "absl/strings/str_cat.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
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;
result |= (static_cast<std::int32_t>(int_bytes[1]) & 0x0FF) << 16;
result |= (static_cast<std::int32_t>(int_bytes[2]) & 0x0FF) << 8;
result |= (static_cast<std::int32_t>(int_bytes[3]) & 0x0FF);
return result;
}
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 ByteArray(int_bytes, sizeof(int_bytes));
}
ExceptionOr<ByteArray> ReadExactly(InputStream* reader, std::int64_t size) {
ByteArray buffer(size);
std::int64_t current_pos = 0;
while (current_pos < size) {
ExceptionOr<ByteArray> read_bytes = reader->Read(size - current_pos);
if (!read_bytes.ok()) {
return read_bytes;
}
ByteArray result = read_bytes.result();
if (result.Empty()) {
return ExceptionOr<ByteArray>(Exception::kIo);
}
buffer.CopyAt(current_pos, result);
current_pos += result.size();
}
return ExceptionOr<ByteArray>(std::move(buffer));
}
ExceptionOr<std::int32_t> ReadInt(InputStream* reader) {
ExceptionOr<ByteArray> read_bytes = ReadExactly(reader, sizeof(std::int32_t));
if (!read_bytes.ok()) {
return ExceptionOr<std::int32_t>(read_bytes.exception());
}
return ExceptionOr<std::int32_t>(BytesToInt(std::move(read_bytes.result())));
}
Exception WriteInt(OutputStream* writer, std::int32_t value) {
return writer->Write(IntToBytes(value));
}
} // namespace
BaseEndpointChannel::BaseEndpointChannel(const std::string& channel_name,
InputStream* reader,
OutputStream* writer)
: channel_name_(channel_name), reader_(reader), writer_(writer) {}
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());
}
// If encryption is enabled, decode the message.
if (IsEncryptionEnabled()) {
MutexLock crypto_lock(&crypto_mutex_);
result = ByteArray(std::move(
*encryption_context_->DecodeMessageFromPeer(std::string(result))));
if (result.Empty()) {
return ExceptionOr<ByteArray>(Exception::kInvalidProtocolBuffer);
}
}
{
MutexLock lock(&last_read_mutex_);
last_read_timestamp_ = SystemClock::ElapsedRealtime();
}
return ExceptionOr<ByteArray>(result);
}
Exception BaseEndpointChannel::Write(const ByteArray& data) {
{
MutexLock pause_lock(&is_paused_mutex_);
if (is_paused_) {
BlockUntilUnpaused();
}
}
ByteArray encrypted_data;
const ByteArray* data_to_write = &data;
{
MutexLock crypto_lock(&crypto_mutex_);
// If encryption is enabled, encode the message.
if (IsEncryptionEnabled()) {
encrypted_data = ByteArray(std::move(
*encryption_context_->EncodeMessageToPeer(std::string(data))));
data_to_write = &encrypted_data;
}
}
{
MutexLock lock(&writer_mutex_);
Exception write_exception =
WriteInt(writer_, static_cast<std::int32_t>(data_to_write->size()));
if (!write_exception.Ok()) {
return write_exception;
}
write_exception = writer_->Write(*data_to_write);
if (write_exception.Ok()) {
return write_exception;
}
Exception flush_exception = writer_->Flush();
if (!flush_exception.Ok()) {
return flush_exception;
}
}
return {Exception::kSuccess};
}
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.
}
}
{
// 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.
}
}
}
void BaseEndpointChannel::Close(
proto::connections::DisconnectionReason reason) {
Close();
}
std::string BaseEndpointChannel::GetType() const {
std::string subtype = IsEncryptionEnabled() ? "ENCRYPTED_" : "";
switch (GetMedium()) {
case proto::connections::Medium::BLUETOOTH:
return absl::StrCat(subtype, "BLUETOOTH");
case proto::connections::Medium::BLE:
return absl::StrCat(subtype, "BLE");
case proto::connections::Medium::MDNS:
return absl::StrCat(subtype, "MDNS");
case proto::connections::Medium::WIFI_HOTSPOT:
return absl::StrCat(subtype, "WIFI_HOTSPOT");
case proto::connections::Medium::WIFI_LAN:
return absl::StrCat(subtype, "WIFI_LAN");
default:
return "UNKNOWN";
}
}
std::string BaseEndpointChannel::GetName() const { return channel_name_; }
void BaseEndpointChannel::EnableEncryption(
securegcm::D2DConnectionContextV1* encryption_context) {
MutexLock lock(&crypto_mutex_);
encryption_context_ = encryption_context;
}
bool BaseEndpointChannel::IsPaused() const {
MutexLock lock(&is_paused_mutex_);
return is_paused_;
}
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() const {
return encryption_context_ != nullptr;
}
void BaseEndpointChannel::BlockUntilUnpaused() {
// For more on how this works, see
// https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
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
@@ -0,0 +1,127 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_BASE_ENDPOINT_CHANNEL_H_
#define CORE_V2_INTERNAL_BASE_ENDPOINT_CHANNEL_H_
#include <cstdint>
#include <string>
#include "core_v2/internal/endpoint_channel.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/public/atomic_reference.h"
#include "platform_v2/public/condition_variable.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/system_clock.h"
#include "proto/connections_enums.pb.h"
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
namespace connections {
class BaseEndpointChannel : public EndpointChannel {
public:
BaseEndpointChannel(const std::string& channel_name, InputStream* reader,
OutputStream* writer);
~BaseEndpointChannel() override = default;
ExceptionOr<ByteArray> Read()
ABSL_LOCKS_EXCLUDED(reader_mutex_, crypto_mutex_,
last_read_mutex_) 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() 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;
// Returns a one-word type descriptor for the concrete EndpointChannel
// implementation that can be used in log messages; eg: BLUETOOTH, BLE,
// WIFI.
std::string GetType() const override;
// Returns the name of the EndpointChannel.
std::string GetName() const override;
// Enables encryption on the EndpointChannel.
// Should be called after connection is accepted by both parties, and
// before entering data phase, where Payloads may be exchanged.
void EnableEncryption(securegcm::D2DConnectionContextV1* context) override;
// True if the EndpointChannel is currently pausing all writes.
bool IsPaused() const ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
// Pauses all writes on this EndpointChannel until resume() is called.
void Pause() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
// Resumes any writes on this EndpointChannel that were suspended when pause()
// was called.
void Resume() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) 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;
private:
// Used to sanity check that our frame sizes are reasonable.
static constexpr std::int32_t kMaxAllowedReadBytes = 1048576; // 1MB
bool IsEncryptionEnabled() const;
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;
// 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.
Mutex reader_mutex_;
InputStream* reader_ ABSL_PT_GUARDED_BY(reader_mutex_);
Mutex writer_mutex_;
OutputStream* writer_ ABSL_PT_GUARDED_BY(writer_mutex_);
// Used by both read and write to protect payload encryption/decryption.
Mutex crypto_mutex_;
// An encryptor/decryptor. May be null.
securegcm::D2DConnectionContextV1* encryption_context_
ABSL_PT_GUARDED_BY(crypto_mutex_) = nullptr;
mutable Mutex is_paused_mutex_;
ConditionVariable is_paused_cond_{&is_paused_mutex_};
// If true, writes should block until this has been set to false.
bool is_paused_ ABSL_GUARDED_BY(is_paused_mutex_) = false;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_BASE_ENDPOINT_CHANNEL_H_
@@ -0,0 +1,356 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/base_endpoint_channel.h"
#include <utility>
#include "core_v2/internal/encryption_runner.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/multi_thread_executor.h"
#include "platform_v2/public/pipe.h"
#include "platform_v2/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;
class TestEndpointChannel : public BaseEndpointChannel {
public:
explicit TestEndpointChannel(InputStream* input, OutputStream* output)
: BaseEndpointChannel("channel", input, output) {}
MOCK_METHOD(Medium, GetMedium, (), (const override));
MOCK_METHOD(void, CloseImpl, (), (override));
};
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::unique_ptr<securegcm::D2DConnectionContextV1>,
std::unique_ptr<securegcm::D2DConnectionContextV1>>
DoDhKeyExchange(BaseEndpointChannel* channel_a,
BaseEndpointChannel* channel_b) {
std::unique_ptr<securegcm::D2DConnectionContextV1> context_a;
std::unique_ptr<securegcm::D2DConnectionContextV1> 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.get());
channel_b.EnableEncryption(context_b.get());
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());
});
std::atomic_bool done = false;
ByteArray read_more;
pause_resume_executor.Execute([&channel_b, &read_more, &done](){
// Read will block until channel is resumed, or closed.
auto response = channel_b.Read();
EXPECT_TRUE(response.ok());
read_more = std::move(response.result());
done = true;
});
absl::SleepFor(absl::Milliseconds(500));
EXPECT_TRUE(read_more.Empty());
// Resume; verify that data transfer comepleted.
channel_a.Resume();
absl::SleepFor(absl::Milliseconds(500));
EXPECT_TRUE(done);
EXPECT_EQ(read_more, more_message);
// Shutdown test environment.
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
}
TEST(BaseEndpointChannelTest, ReadAfterInputStreamClosed) {
Pipe pipe;
InputStream& input_stream = pipe.GetInputStream();
OutputStream& output_stream = pipe.GetOutputStream();
TestEndpointChannel test_channel(&input_stream, &output_stream);
// Close the output stream before trying to read from the input.
output_stream.Close();
// Trying to read should fail gracefully with an IO error.
ExceptionOr<ByteArray> read_data = test_channel.Read();
ASSERT_FALSE(read_data.ok());
ASSERT_TRUE(read_data.GetException().Raised(Exception::kIo));
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/base_pcp_handler.h"
#include <cassert>
#include <cinttypes>
#include <cstdlib>
#include <limits>
#include <memory>
#include "core_v2/internal/offline_frames.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/system_clock.h"
#include "securegcm/d2d_connection_context_v1.h"
#include "securegcm/ukey2_handshake.h"
#include "absl/container/flat_hash_set.h"
#include "absl/types/span.h"
namespace location {
namespace nearby {
namespace connections {
BasePcpHandler::BasePcpHandler(EndpointManager* endpoint_manager,
EndpointChannelManager* channel_manager)
: endpoint_manager_(endpoint_manager), channel_manager_(channel_manager) {}
BasePcpHandler::~BasePcpHandler() {
// Unregister ourselves from the FrameProcessors.
endpoint_manager_->UnregisterFrameProcessor(V1Frame::CONNECTION_RESPONSE,
handle_);
// Stop all the ongoing Runnables (as gracefully as possible).
serial_executor_.Shutdown();
alarm_executor_.Shutdown();
}
Status BasePcpHandler::StartAdvertising(ClientProxy* client,
const string& service_id,
const ConnectionOptions& options,
const ConnectionRequestInfo& info) {
Future<Status> response;
RunOnPcpHandlerThread(
[this, client, &service_id, &info, &options, &response]() {
auto result = StartAdvertisingImpl(client, service_id,
client->GenerateLocalEndpointId(),
info.name, options);
if (!result.status.Ok()) {
response.Set(result.status);
return;
}
// Now that we've succeeded, mark the client as advertising.
advertising_options_ = options;
advertising_listener_ = info.listener;
client->StartedAdvertising(service_id, GetStrategy(), info.listener,
absl::MakeSpan(result.mediums));
response.Set({Status::kSuccess});
});
return WaitForResult(absl::StrCat("StartAdvertising(", info.name, ")"),
client->GetClientId(), &response);
}
void BasePcpHandler::StopAdvertising(ClientProxy* client) {
CountDownLatch latch(1);
RunOnPcpHandlerThread([this, client, &latch]() {
StopAdvertisingImpl(client);
client->StoppedAdvertising();
advertising_options_.Clear();
latch.CountDown();
});
WaitForLatch("StopAdvertising", &latch);
}
Status BasePcpHandler::StartDiscovery(ClientProxy* client,
const string& service_id,
const ConnectionOptions& options,
const DiscoveryListener& listener) {
Future<Status> response;
RunOnPcpHandlerThread(
[this, client, service_id, options, listener, &response]() {
// Ask the implementation to attempt to start discovery.
auto result = StartDiscoveryImpl(client, service_id, options);
if (!result.status.Ok()) {
response.Set(result.status);
return;
}
// Now that we've succeeded, mark the client as discovering and clear
// out any old endpoints we had discovered.
discovery_options_ = options;
discovered_endpoints_.clear();
client->StartedDiscovery(service_id, GetStrategy(), listener,
absl::MakeSpan(result.mediums));
response.Set({Status::kSuccess});
});
return WaitForResult(absl::StrCat("StartDiscovery(", service_id, ")"),
client->GetClientId(), &response);
}
void BasePcpHandler::StopDiscovery(ClientProxy* client) {
CountDownLatch latch(1);
RunOnPcpHandlerThread([this, client, &latch]() {
StopDiscoveryImpl(client);
client->StoppedDiscovery();
discovery_options_.Clear();
latch.CountDown();
});
WaitForLatch("stopDiscovery", &latch);
}
void BasePcpHandler::WaitForLatch(const string& method_name,
CountDownLatch* latch) {
Exception await_exception = latch->Await();
if (!await_exception.Ok()) {
if (await_exception.Raised(Exception::kTimeout)) {
NEARBY_LOG(INFO, "Blocked in %s", method_name.c_str());
}
}
}
Status BasePcpHandler::WaitForResult(const string& method_name,
std::int64_t client_id,
Future<Status>* future) {
if (!future) {
NEARBY_LOG(INFO, "No future to wait for; return with error");
return {Status::kError};
}
NEARBY_LOG(INFO, "waiting for future to complete");
ExceptionOr<Status> result = future->Get();
if (!result.ok()) {
NEARBY_LOG(INFO, "Future completed with exception: %d", result.exception());
return {Status::kError};
}
NEARBY_LOG(INFO, "Future completed with status: %d", result.result().value);
return result.result();
}
void BasePcpHandler::RunOnPcpHandlerThread(Runnable runnable) {
serial_executor_.Execute(std::move(runnable));
}
} // namespace connections
} // namespace nearby
} // namespace location
+337
View File
@@ -0,0 +1,337 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_BASE_PCP_HANDLER_H_
#define CORE_V2_INTERNAL_BASE_PCP_HANDLER_H_
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "core_v2/internal/client_proxy.h"
#include "core_v2/internal/encryption_runner.h"
#include "core_v2/internal/endpoint_channel_manager.h"
#include "core_v2/internal/endpoint_manager.h"
#include "core_v2/internal/pcp.h"
#include "core_v2/internal/pcp_handler.h"
#include "core_v2/listeners.h"
#include "core_v2/options.h"
#include "core_v2/status.h"
#include "proto/connections/offline_wire_formats.pb.h"
#include "platform_v2/base/prng.h"
#include "platform_v2/public/atomic_reference.h"
#include "platform_v2/public/cancelable_alarm.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/future.h"
#include "platform_v2/public/scheduled_executor.h"
#include "platform_v2/public/single_thread_executor.h"
#include "platform_v2/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/flat_hash_map.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace connections {
// 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; }
private:
T* pointer_ = nullptr;
};
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
// this class, so that they can focus exclusively on the medium-specific
// operations.
class BasePcpHandler : public PcpHandler,
public EndpointManager::FrameProcessor {
public:
using FrameProcessor = EndpointManager::FrameProcessor;
// TODO(tracyzhou): Add SecureRandom.
BasePcpHandler(EndpointManager* endpoint_manager,
EndpointChannelManager* channel_manager);
~BasePcpHandler() override;
BasePcpHandler(BasePcpHandler&&) = delete;
BasePcpHandler& operator=(BasePcpHandler&&) = delete;
// We have been asked by the client to start advertising. Once we successfully
// start advertising, we'll change the ClientProxy's state.
// ConnectionListener (info.listener) will be notified in case of any event.
// See for details
// cpp/core_v2/listeners.h
Status StartAdvertising(ClientProxy* client_proxy,
const std::string& service_id,
const ConnectionOptions& options,
const ConnectionRequestInfo& info) override;
// If Advertising is active, stop it, and change CLientProxy state,
// otherwise do nothing.
void StopAdvertising(ClientProxy* client_proxy) override;
// Start discovery of endpoints that may be advertising.
// Update ClientProxy state once discovery started.
// DiscoveryListener will get called in case of any event.
Status StartDiscovery(ClientProxy* client_proxy,
const std::string& service_id,
const ConnectionOptions& options,
const DiscoveryListener& listener) override;
// If Discovery is active, stop it, and change CLientProxy state,
// otherwise do nothing.
void StopDiscovery(ClientProxy* client_proxy) override;
// If remote endpoint has been successfully discovered, request it to form a
// connection, update state on ClientProxy.
Status RequestConnection(ClientProxy* client_proxy,
const std::string& endpoint_id,
const ConnectionRequestInfo& info) override {
return Status{Status::kError};
}
// Either party may call this to accept connection on their part.
// Until both parties call it, connection will not reach a data phase.
// Update state in ClientProxy.
Status AcceptConnection(ClientProxy* client_proxy,
const std::string& endpoint_id,
const PayloadListener& payload_listener) override {
return Status{Status::kError};
}
// Either party may call this to accept connection on their part.
// If either party does call it, connection will terminate.
// Update state in ClientProxy.
Status RejectConnection(ClientProxy* client_proxy,
const std::string& endpoint_id) override {
return Status{Status::kError};
}
// @EndpointManagerReaderThread
void OnIncomingFrame(const 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 OnEndpointDisconnect(ClientProxy* client_proxy,
const std::string& endpoint_id,
CountDownLatch* barrier) override {}
protected:
// The result of a call to startAdvertisingImpl() or startDiscoveryImpl().
struct StartOperationResult {
Status status;
// If success, the mediums on which we are now advertising/discovering, for
// analytics.
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() = default;
virtual std::string GetEndpointId() const = 0;
virtual std::string GetEndpointName() const = 0;
virtual std::string GetServiceId() const = 0;
virtual proto::connections::Medium GetMedium() const = 0;
};
struct ConnectImplResult {
proto::connections::Medium medium =
proto::connections::Medium::UNKNOWN_MEDIUM;
Status status = {Status::kError};
std::unique_ptr<EndpointChannel> endpoint_channel;
};
void RunOnPcpHandlerThread(Runnable runnable);
ConnectionOptions GetConnectionOptions() const;
// @PcpHandlerThread
void OnEndpointFound(ClientProxy* client_proxy,
std::unique_ptr<DiscoveredEndpoint> endpoint);
// @PcpHandlerThread
void OnEndpointLost(ClientProxy* client_proxy,
const DiscoveredEndpoint* endpoint);
Exception OnIncomingConnection(
ClientProxy* client_proxy, const std::string& remote_device_name,
std::unique_ptr<EndpointChannel> endpoint_channel,
proto::connections::Medium medium); // throws Exception::IO
// @PcpHandlerThread
virtual StartOperationResult StartAdvertisingImpl(
ClientProxy* client_proxy, const std::string& service_id,
const std::string& local_endpoint_id,
const std::string& local_endpoint_name,
const ConnectionOptions& options) = 0;
// @PcpHandlerThread
virtual Status StopAdvertisingImpl(ClientProxy* client_proxy) = 0;
// @PcpHandlerThread
virtual StartOperationResult StartDiscoveryImpl(
ClientProxy* client_proxy, const std::string& service_id,
const ConnectionOptions& options) = 0;
// @PcpHandlerThread
virtual Status StopDiscoveryImpl(ClientProxy* client_proxy) = 0;
// @PcpHandlerThread
virtual ConnectImplResult ConnectImpl(ClientProxy* client_proxy,
DiscoveredEndpoint* endpoint) = 0;
virtual std::vector<proto::connections::Medium>
GetConnectionMediumsByPriority() = 0;
virtual proto::connections::Medium GetDefaultUpgradeMedium() = 0;
EndpointManager* endpoint_manager_;
EndpointChannelManager* channel_manager_;
private:
static Exception WriteConnectionRequestFrame(
EndpointChannel* endpoint_channel, const std::string& local_endpoint_id,
const std::string& local_endpoint_name, std::int32_t nonce,
const std::vector<proto::connections::Medium>& supported_mediums);
static constexpr absl::Duration kConnectionRequestReadTimeout =
absl::Seconds(2);
static constexpr absl::Duration kRejectedConnectionCloseDelay =
absl::Seconds(2);
void OnConnectionResponse(ClientProxy* client_proxy,
const std::string& endpoint_id,
const OfflineFrame& frame);
// Returns true if the new endpoint is preferred over the old endpoint.
bool IsPreferred(const BasePcpHandler::DiscoveredEndpoint& new_endpoint,
const BasePcpHandler::DiscoveredEndpoint& old_endpoint);
// 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
// for outgoing connections and older devices that don't report their
// supported mediums.
void InitiateBandwidthUpgrade(
ClientProxy* client_proxy, 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>& supported_mediums);
void ProcessPreConnectionInitiationFailure(const std::string& endpoint_id,
EndpointChannel* channel,
Status status,
Future<Status>* result);
void ProcessPreConnectionResultFailure(ClientProxy* client_proxy,
const std::string& endpoint_id);
DiscoveredEndpoint* GetDiscoveredEndpoint(const std::string& endpoint_id);
// Called when either side accepts/rejects the connection, but only takes
// effect after both have accepted or one side has rejected.
//
// 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(ClientProxy* client_proxy,
const std::string& endpoint_id,
bool can_close_immediately);
ExceptionOr<OfflineFrame> ReadConnectionRequestFrame(
EndpointChannel* channel);
void WaitForLatch(const std::string& method_name, CountDownLatch* latch);
Status WaitForResult(const std::string& method_name, std::int64_t client_id,
Future<Status>* future);
AtomicReference<proto::connections::Medium> bandwidth_upgrade_medium_{
proto::connections::Medium::UNKNOWN_MEDIUM};
ScheduledExecutor alarm_executor_;
SingleThreadExecutor serial_executor_;
// A map of endpoint id -> DiscoveredEndpoint.
absl::flat_hash_map<std::string, std::unique_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.
absl::flat_hash_map<std::string, CancelableAlarm> pending_alarms_;
// 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.
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.)
ConnectionOptions discovery_options_;
Prng prng_;
EncryptionRunner encryption_runner_;
EndpointManager::FrameProcessor::Handle handle_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_BASE_PCP_HANDLER_H_
@@ -0,0 +1,301 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/base_pcp_handler.h"
#include <memory>
#include "core_v2/internal/base_endpoint_channel.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/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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
using ::location::nearby::proto::connections::Medium;
using ::testing::_;
using ::testing::Invoke;
using ::testing::MockFunction;
using ::testing::Return;
using ::testing::StrictMock;
class MockEndpointChannel : public BaseEndpointChannel {
public:
explicit MockEndpointChannel(Pipe* reader, Pipe* writer)
: BaseEndpointChannel("channel", &reader->GetInputStream(),
&writer->GetOutputStream()) {}
ExceptionOr<ByteArray> DoRead() { return BaseEndpointChannel::Read(); }
Exception DoWrite(const ByteArray& data) {
return BaseEndpointChannel::Write(data);
}
absl::Time DoGetLastReadTimestamp() {
return BaseEndpointChannel::GetLastReadTimestamp();
}
MOCK_METHOD(ExceptionOr<ByteArray>, Read, (), (override));
MOCK_METHOD(Exception, Write, (const ByteArray& data), (override));
MOCK_METHOD(void, CloseImpl, (), (override));
MOCK_METHOD(proto::connections::Medium, GetMedium, (), (const override));
MOCK_METHOD(std::string, GetType, (), (const override));
MOCK_METHOD(std::string, GetName, (), (const override));
MOCK_METHOD(bool, IsPaused, (), (const override));
MOCK_METHOD(void, Pause, (), (override));
MOCK_METHOD(void, Resume, (), (override));
MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const override));
};
class MockPcpHandler : public BasePcpHandler {
public:
MockPcpHandler(EndpointManager* em, EndpointChannelManager* ecm)
: BasePcpHandler(em, ecm) {}
// Expose protected inner types of a base type for mocking.
using BasePcpHandler::ConnectImplResult;
using BasePcpHandler::DiscoveredEndpoint;
using BasePcpHandler::StartOperationResult;
MOCK_METHOD(Strategy, GetStrategy, (), (override));
MOCK_METHOD(Pcp, GetPcp, (), (override));
MOCK_METHOD(StartOperationResult, StartAdvertisingImpl,
(ClientProxy * client, const string& service_id,
const string& local_endpoint_id,
const string& local_endpoint_name,
const ConnectionOptions& options),
(override));
MOCK_METHOD(Status, StopAdvertisingImpl, (ClientProxy * client), (override));
MOCK_METHOD(StartOperationResult, StartDiscoveryImpl,
(ClientProxy * client, const string& service_id,
const ConnectionOptions& options),
(override));
MOCK_METHOD(Status, StopDiscoveryImpl, (ClientProxy * client), (override));
MOCK_METHOD(ConnectImplResult, ConnectImpl,
(ClientProxy * client, DiscoveredEndpoint* endpoint), (override));
MOCK_METHOD(std::vector<proto::connections::Medium>,
GetConnectionMediumsByPriority, (), (override));
MOCK_METHOD(proto::connections::Medium, GetDefaultUpgradeMedium, (),
(override));
// Mock adapters for protected non-virtual methods of a base class.
void OnEndpointFound(ClientProxy* client,
std::unique_ptr<DiscoveredEndpoint> endpoint) {
BasePcpHandler::OnEndpointFound(client, std::move(endpoint));
}
void OnEndpointLost(ClientProxy* client, DiscoveredEndpoint* endpoint) {
BasePcpHandler::OnEndpointLost(client, endpoint);
}
};
class MockDiscoveredEndpoint final : public MockPcpHandler::DiscoveredEndpoint {
public:
MOCK_METHOD(std::string, GetEndpointId, (), (const override));
MOCK_METHOD(std::string, GetEndpointName, (), (const override));
MOCK_METHOD(std::string, GetServiceId, (), (const override));
MOCK_METHOD(Medium, GetMedium, (), (const override));
};
class BasePcpHandlerTest : public ::testing::Test {
protected:
struct MockConnectionListener {
StrictMock<MockFunction<void(const std::string& endpoint_id,
const ConnectionResponseInfo& info)>>
initiated_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>> accepted_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
const Status& status)>>
rejected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>>
disconnected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
std::int32_t quality)>>
bandwidth_changed_cb;
};
struct MockDiscoveryListener {
StrictMock<MockFunction<void(const std::string& endpoint_id,
const std::string& endpoint_name,
const std::string& service_id)>>
endpoint_found_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>>
endpoint_lost_cb;
StrictMock<
MockFunction<void(const std::string& endpoint_id, DistanceInfo info)>>
endpoint_distance_changed_cb;
};
void StartAdvertising(ClientProxy* client, MockPcpHandler* pcp_handler) {
std::string service_id{"service"};
ConnectionOptions options{
.strategy = Strategy::kP2pCluster,
.auto_upgrade_bandwidth = true,
.enforce_topology_constraints = true,
};
ConnectionRequestInfo info{
.name = "remote_endpoint_name",
.listener = connection_listener_,
};
EXPECT_CALL(*pcp_handler,
StartAdvertisingImpl(client, service_id, _, info.name, _))
.WillOnce(Return(MockPcpHandler::StartOperationResult{
.status = {Status::kSuccess},
.mediums = {Medium::BLE},
}));
EXPECT_EQ(pcp_handler->StartAdvertising(client, service_id, options, info),
Status{Status::kSuccess});
EXPECT_TRUE(client->IsAdvertising());
}
void StartDiscovery(ClientProxy* client, MockPcpHandler* pcp_handler) {
std::string service_id{"service"};
ConnectionOptions options{
.strategy = Strategy::kP2pCluster,
.auto_upgrade_bandwidth = true,
.enforce_topology_constraints = true,
};
EXPECT_CALL(*pcp_handler, StartDiscoveryImpl(client, service_id, _))
.WillOnce(Return(MockPcpHandler::StartOperationResult{
.status = {Status::kSuccess},
.mediums = {Medium::BLE},
}));
EXPECT_EQ(pcp_handler->StartDiscovery(client, service_id, options,
discovery_listener_),
Status{Status::kSuccess});
EXPECT_TRUE(client->IsDiscovering());
}
std::pair<std::unique_ptr<MockEndpointChannel>,
std::unique_ptr<MockEndpointChannel>>
SetupConnection(Pipe& pipe_a, Pipe& pipe_b) { // NOLINT
auto channel_a = std::make_unique<MockEndpointChannel>(&pipe_b, &pipe_a);
auto channel_b = std::make_unique<MockEndpointChannel>(&pipe_a, &pipe_b);
// On initiator (A) side, we drop the first write, since this is a
// connection establishment packet, and we don't have the peer entity, just
// the peer channel. The rest of the exchange must happen for the benefit of
// DH key exchange.
EXPECT_CALL(*channel_a, Read())
.WillRepeatedly(Invoke(
[channel = channel_a.get()]() { return channel->DoRead(); }));
EXPECT_CALL(*channel_a, Write(_))
.WillOnce(Return(Exception{Exception::kSuccess}))
.WillRepeatedly(
Invoke([channel = channel_a.get()](const ByteArray& data) {
return channel->DoWrite(data);
}));
EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(Medium::BLE));
EXPECT_CALL(*channel_a, GetLastReadTimestamp)
.WillRepeatedly(Return(absl::Now()));
EXPECT_CALL(*channel_a, IsPaused)
.WillRepeatedly(Return(false));
EXPECT_CALL(*channel_b, Read())
.WillRepeatedly(Invoke(
[channel = channel_b.get()]() { return channel->DoRead(); }));
EXPECT_CALL(*channel_b, Write(_))
.WillRepeatedly(
Invoke([channel = channel_b.get()](const ByteArray& data) {
return channel->DoWrite(data);
}));
EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(Medium::BLE));
EXPECT_CALL(*channel_b, GetLastReadTimestamp)
.WillRepeatedly(Return(absl::Now()));
EXPECT_CALL(*channel_b, IsPaused)
.WillRepeatedly(Return(false));
return std::make_pair(std::move(channel_a), std::move(channel_b));
}
Pipe pipe_a_;
Pipe pipe_b_;
MockConnectionListener mock_connection_listener_;
MockDiscoveryListener mock_discovery_listener_;
ConnectionListener connection_listener_{
.initiated_cb = mock_connection_listener_.initiated_cb.AsStdFunction(),
.accepted_cb = mock_connection_listener_.accepted_cb.AsStdFunction(),
.rejected_cb = mock_connection_listener_.rejected_cb.AsStdFunction(),
.disconnected_cb =
mock_connection_listener_.disconnected_cb.AsStdFunction(),
.bandwidth_changed_cb =
mock_connection_listener_.bandwidth_changed_cb.AsStdFunction(),
};
DiscoveryListener discovery_listener_{
.endpoint_found_cb =
mock_discovery_listener_.endpoint_found_cb.AsStdFunction(),
.endpoint_lost_cb =
mock_discovery_listener_.endpoint_lost_cb.AsStdFunction(),
.endpoint_distance_changed_cb =
mock_discovery_listener_.endpoint_distance_changed_cb.AsStdFunction(),
};
};
TEST_F(BasePcpHandlerTest, ConstructorDestructorWorks) {
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(em.get(), ecm.get());
SUCCEED();
}
TEST_F(BasePcpHandlerTest, StartAdvertisingChangesState) {
auto client = std::make_unique<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(em.get(), ecm.get());
StartAdvertising(client.get(), pcp_handler.get());
}
TEST_F(BasePcpHandlerTest, StopAdvertisingChangesState) {
auto client = std::make_unique<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(em.get(), ecm.get());
StartAdvertising(client.get(), pcp_handler.get());
EXPECT_CALL(*pcp_handler, StopAdvertisingImpl(client.get())).Times(1);
EXPECT_TRUE(client->IsAdvertising());
pcp_handler->StopAdvertising(client.get());
EXPECT_FALSE(client->IsAdvertising());
}
TEST_F(BasePcpHandlerTest, StartDiscoveryChangesState) {
auto client = std::make_unique<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(em.get(), ecm.get());
StartDiscovery(client.get(), pcp_handler.get());
}
TEST_F(BasePcpHandlerTest, StopDiscoveryChangesState) {
auto client = std::make_unique<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(em.get(), ecm.get());
StartDiscovery(client.get(), pcp_handler.get());
EXPECT_CALL(*pcp_handler, StopDiscoveryImpl(client.get())).Times(1);
EXPECT_TRUE(client->IsDiscovering());
pcp_handler->StopDiscovery(client.get());
EXPECT_FALSE(client->IsDiscovering());
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+236
View File
@@ -0,0 +1,236 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/ble_advertisement.h"
#include <inttypes.h>
#include "platform_v2/public/logging.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
namespace connections {
BleAdvertisement::BleAdvertisement(Version version, Pcp pcp,
const ByteArray& service_id_hash,
const std::string& endpoint_id,
const std::string& endpoint_name,
const std::string& bluetooth_mac_address) {
if (version != Version::kV1 ||
service_id_hash.size() != kServiceIdHashLength || endpoint_id.empty() ||
endpoint_id.length() != kEndpointIdLength ||
endpoint_name.length() > kMaxEndpointNameLength) {
return;
}
switch (pcp) {
case Pcp::kP2pCluster: // Fall through
case Pcp::kP2pStar: // Fall through
case Pcp::kP2pPointToPoint:
break;
default:
return;
}
version_ = version;
pcp_ = pcp;
service_id_hash_ = service_id_hash;
endpoint_id_ = endpoint_id;
endpoint_name_ = endpoint_name;
if (!BluetoothMacAddressHexStringToBytes(bluetooth_mac_address).Empty()) {
bluetooth_mac_address_ = bluetooth_mac_address;
}
}
BleAdvertisement::BleAdvertisement(const ByteArray& ble_advertisement_bytes) {
if (ble_advertisement_bytes.Empty()) {
NEARBY_LOG(ERROR,
"Cannot deserialize BleAdvertisement: null bytes passed in.");
return;
}
if (ble_advertisement_bytes.size() < kMinAdvertisementLength) {
NEARBY_LOG(ERROR,
"Cannot deserialize BleAdvertisement: expecting min %d raw "
"bytes, got %" PRIu64,
kMinAdvertisementLength, ble_advertisement_bytes.size());
return;
}
// Start reading the bytes.
auto* ble_advertisement_bytes_read_ptr = ble_advertisement_bytes.data();
// The first 3 bits are supposed to be the version.
version_ = static_cast<Version>(
(*ble_advertisement_bytes_read_ptr & kVersionBitmask) >> 5);
if (version_ != Version::kV1) {
NEARBY_LOG(ERROR,
"Cannot deserialize BleAdvertisement: unsupported Version %d",
version_);
return;
}
pcp_ = static_cast<Pcp>(*ble_advertisement_bytes_read_ptr & kPcpBitmask);
ble_advertisement_bytes_read_ptr++;
switch (pcp_) {
case Pcp::kP2pCluster: // Fall through
case Pcp::kP2pStar: // Fall through
case Pcp::kP2pPointToPoint: {
// The next 24 bits are supposed to be the service_id_hash.
service_id_hash_ =
ByteArray(ble_advertisement_bytes_read_ptr, kServiceIdHashLength);
ble_advertisement_bytes_read_ptr += kServiceIdHashLength;
// The next 32 bits are supposed to be the endpoint_id.
endpoint_id_ =
std::string(ble_advertisement_bytes_read_ptr, kEndpointIdLength);
ble_advertisement_bytes_read_ptr += kEndpointIdLength;
// The next 8 bits are the length of the endpoint name.
auto expected_endpoint_name_length = static_cast<std::uint32_t>(
*ble_advertisement_bytes_read_ptr & kEndpointNameLengthBitmask);
ble_advertisement_bytes_read_ptr++;
// The next x bits are the endpoint name. (Max length is 131 bytes).
// Check that the stated endpoint_name_length is the same as what we
// received (based off of the length of ble_advertisement_bytes).
auto actual_endpoint_name_length =
ComputeEndpointNameLength(ble_advertisement_bytes);
if (actual_endpoint_name_length < expected_endpoint_name_length) {
NEARBY_LOG(
ERROR,
"Cannot deserialize BleAdvertisement: expected endpointName to "
"be %d bytes, got %d bytes",
expected_endpoint_name_length, actual_endpoint_name_length);
// Clear enpoint_id for validadity.
endpoint_id_.clear();
return;
}
endpoint_name_ = std::string(ble_advertisement_bytes_read_ptr,
expected_endpoint_name_length);
ble_advertisement_bytes_read_ptr += expected_endpoint_name_length;
// The next 48 bits are the bluetooth mac address.
auto bluetooth_mac_address_bytes = ByteArray(
ble_advertisement_bytes_read_ptr, kBluetoothMacAddressLength);
// If the Bluetooth MAC Address bytes are unset or invalid, leave the
// string empty. Otherwise, convert it to the proper colon delimited
// format.
if (!IsBluetoothMacAddressUnset(bluetooth_mac_address_bytes)) {
bluetooth_mac_address_ =
HexBytesToColonDelimitedString(bluetooth_mac_address_bytes);
}
break;
}
default:
// TODO(edwinwu): [ANALYTICIZE] This either represents corruption over
// the air, or older versions of GmsCore intermingling with newer
// ones.
NEARBY_LOG(ERROR,
"Cannot deserialize BleAdvertisement: uunsupported V1 PCP %d",
pcp_);
break;
}
}
BleAdvertisement::operator ByteArray() const {
if (!IsValid()) {
return ByteArray();
}
std::string out;
// 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;
out.reserve(1 + service_id_hash_.size() + kEndpointIdLength + 1 +
endpoint_name_.size() + kBluetoothMacAddressLength);
out.append(1, version_and_pcp_byte);
out.append(std::string(service_id_hash_));
out.append(endpoint_id_);
out.append(1, endpoint_name_.size());
out.append(endpoint_name_);
// The next 48 bits are the bluetooth mac address. If bluetooth_mac_address is
// invalid or empty, we get back a null byte array.
auto bluetooth_mac_address_bytes(
BluetoothMacAddressHexStringToBytes(bluetooth_mac_address_));
if (!bluetooth_mac_address_bytes.Empty()) {
out.append(bluetooth_mac_address_bytes.data(), kBluetoothMacAddressLength);
}
return ByteArray(std::move(out));
}
std::uint32_t BleAdvertisement::ComputeEndpointNameLength(
const ByteArray& ble_advertisement_bytes) const {
return ble_advertisement_bytes.size() - kMinAdvertisementLength;
}
ByteArray BleAdvertisement::BluetoothMacAddressHexStringToBytes(
const std::string& bluetooth_mac_address) const {
std::string bt_mac_address(bluetooth_mac_address);
// Remove the colon delimiters.
bt_mac_address.erase(
std::remove(bt_mac_address.begin(), bt_mac_address.end(), ':'),
bt_mac_address.end());
// If the bluetooth mac address is invalid (wrong size), return a null byte
// array.
if (bt_mac_address.length() != kBluetoothMacAddressLength * 2) {
return ByteArray();
}
// Convert to bytes. If MAC Address bytes are unset, return a null byte array.
auto bt_mac_address_string(absl::HexStringToBytes(bt_mac_address));
auto bt_mac_address_bytes =
ByteArray(bt_mac_address_string.data(), bt_mac_address_string.size());
if (IsBluetoothMacAddressUnset(bt_mac_address_bytes)) {
return ByteArray();
}
return bt_mac_address_bytes;
}
std::string BleAdvertisement::HexBytesToColonDelimitedString(
const ByteArray& hex_bytes) const {
// Convert the hex bytes to a string.
std::string colon_delimited_string(
absl::BytesToHexString(std::string(hex_bytes.data(), hex_bytes.size())));
absl::AsciiStrToUpper(&colon_delimited_string);
// Insert the colons.
for (int i = colon_delimited_string.length() - 2; i > 0; i -= 2) {
colon_delimited_string.insert(i, ":");
}
return colon_delimited_string;
}
bool BleAdvertisement::IsBluetoothMacAddressUnset(
const ByteArray& bluetooth_mac_address_bytes) const {
for (int i = 0; i < bluetooth_mac_address_bytes.size(); i++) {
if (bluetooth_mac_address_bytes.data()[i] != 0) {
return false;
}
}
return true;
}
} // namespace connections
} // namespace nearby
} // namespace location
+104
View File
@@ -0,0 +1,104 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_BLE_ADVERTISEMENT_H_
#define CORE_V2_INTERNAL_BLE_ADVERTISEMENT_H_
#include "core_v2/internal/pcp.h"
#include "platform_v2/base/byte_array.h"
namespace location {
namespace nearby {
namespace connections {
// 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>See go/connections-ble-advertisement for more information.
class BleAdvertisement {
public:
// 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 constexpr int kServiceIdHashLength = 3;
static constexpr int kVersionAndPcpLength = 1;
// Should be defined as EndpointManager<Platform>::kEndpointIdLength, but that
// involves making BleAdvertisement templatized on Platform just for
// that one little thing, so forget it (at least for now).
static constexpr int kEndpointIdLength = 4;
static constexpr int kEndpointNameSizeLength = 1;
static constexpr int kBluetoothMacAddressLength = 6;
static constexpr int kMinAdvertisementLength =
kVersionAndPcpLength + kServiceIdHashLength + kEndpointIdLength +
kEndpointNameSizeLength + kBluetoothMacAddressLength;
static constexpr int kMaxEndpointNameLength = 131;
static constexpr int kVersionBitmask = 0x0E0;
static constexpr int kPcpBitmask = 0x01F;
static constexpr int kEndpointNameLengthBitmask = 0x0FF;
BleAdvertisement() = default;
BleAdvertisement(Version version, Pcp pcp, const ByteArray& service_id_hash,
const std::string& endpoint_id,
const std::string& endpoint_name,
const std::string& bluetooth_mac_address);
explicit BleAdvertisement(const ByteArray& ble_advertisement_bytes);
~BleAdvertisement() = default;
BleAdvertisement(const BleAdvertisement&) = default;
BleAdvertisement& operator=(const BleAdvertisement&) = default;
BleAdvertisement(BleAdvertisement&&) = default;
BleAdvertisement& operator=(BleAdvertisement&&) = default;
explicit operator ByteArray() const;
inline bool IsValid() const { return !endpoint_id_.empty(); }
inline Version GetVersion() const { return version_; }
inline Pcp GetPcp() const { return pcp_; }
inline ByteArray GetServiceIdHash() const{ return service_id_hash_; }
inline std::string GetEndpointId() const { return endpoint_id_; }
inline std::string GetEndpointName() const { return endpoint_name_; }
inline std::string GetBluetoothMacAddress() const {
return bluetooth_mac_address_;
}
private:
std::uint32_t ComputeEndpointNameLength(
const ByteArray& ble_advertisement_bytes) const;
ByteArray BluetoothMacAddressHexStringToBytes(
const std::string& bluetooth_mac_address) const;
std::string HexBytesToColonDelimitedString(const ByteArray& hex_bytes) const;
bool IsBluetoothMacAddressUnset(
const ByteArray& bluetooth_mac_address_bytes) const;
Version version_ = Version::kUndefined;
Pcp pcp_ = Pcp::kUnknown;
ByteArray service_id_hash_;
std::string endpoint_id_;
std::string endpoint_name_;
std::string bluetooth_mac_address_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_BLE_ADVERTISEMENT_H_
@@ -0,0 +1,272 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/ble_advertisement.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
const BleAdvertisement::Version kVersion = BleAdvertisement::Version::kV1;
const Pcp kPcp = Pcp::kP2pCluster;
const char kServiceIDHashBytes[] = {0x0A, 0x0B, 0x0C};
const char kEndPointID[] = "AB12";
const char kEndpointName[] =
"How much wood can a woodchuck chuck if a wood chuck would chuck wood?";
const char kBluetoothMacAddress[] = "00:00:E6:88:64:13";
TEST(BleAdvertisementTest, ConstructionWorks) {
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId());
EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName());
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
}
TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointName) {
std::string empty_endpoint_name;
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
empty_endpoint_name, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId());
EXPECT_EQ(empty_endpoint_name, ble_advertisement.GetEndpointName());
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
}
TEST(BleAdvertisementTest, ConstructionWorksWithEmojiEndpointName) {
std::string emoji_endpoint_name("\u0001F450 \u0001F450");
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
emoji_endpoint_name, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId());
EXPECT_EQ(emoji_endpoint_name, ble_advertisement.GetEndpointName());
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
}
TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointName) {
std::string long_endpoint_name(BleAdvertisement::kMaxEndpointNameLength + 1,
'x');
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
long_endpoint_name, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
TEST(BleAdvertisementTest, ConstructionFailsWithBadVersion) {
auto bad_version = static_cast<BleAdvertisement::Version>(666);
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(bad_version, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
TEST(BleAdvertisementTest, ConstructionFailsWithBadPCP) {
auto bad_pcp = static_cast<Pcp>(666);
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, bad_pcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto is_valid = ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
TEST(BleAdvertisementTest, ConstructionSucceedsWithEmptyBluetoothMacAddress) {
std::string empty_bluetooth_mac_address = "";
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, empty_bluetooth_mac_address);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
}
TEST(BleAdvertisementTest, ConstructionSucceedsWithInvalidBluetoothMacAddress) {
std::string bad_bluetooth_mac_address = "022:00";
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, bad_bluetooth_mac_address);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId());
EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName());
EXPECT_TRUE(ble_advertisement.GetBluetoothMacAddress().empty());
}
TEST(BleAdvertisementTest, ConstructionFromBytesWorks) {
// Serialize good data into a good Ble Advertisement.
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto org_ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto ble_advertisement_bytes = ByteArray(org_ble_advertisement);
auto ble_advertisement = BleAdvertisement(ble_advertisement_bytes);
auto is_valid = ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId());
EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName());
EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress());
}
// Bytes at the end should be ignored so that they can be used as reserve bytes
// in the future.
TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) {
// Serialize good data into a good Ble Advertisement.
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto ble_advertisement_bytes = ByteArray(ble_advertisement);
// Add bytes to the end of the valid Ble advertisement.
auto long_ble_advertisement_bytes =
ByteArray(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());
auto long_ble_advertisement = BleAdvertisement(long_ble_advertisement_bytes);
auto is_valid = long_ble_advertisement.IsValid();
EXPECT_TRUE(is_valid);
EXPECT_EQ(kVersion, long_ble_advertisement.GetVersion());
EXPECT_EQ(kPcp, long_ble_advertisement.GetPcp());
EXPECT_EQ(service_id_hash, long_ble_advertisement.GetServiceIdHash());
EXPECT_EQ(kEndPointID, long_ble_advertisement.GetEndpointId());
EXPECT_EQ(kEndpointName, long_ble_advertisement.GetEndpointName());
EXPECT_EQ(kBluetoothMacAddress,
long_ble_advertisement.GetBluetoothMacAddress());
}
TEST(BleAdvertisementTest, ConstructionFromNullBytesFails) {
auto ble_advertisement = BleAdvertisement(ByteArray());
auto is_valid = ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
TEST(BleAdvertisementTest, ConstructionFromShortLengthBytesFails) {
// Serialize good data into a good Ble Advertisement.
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto ble_advertisement_bytes = ByteArray(ble_advertisement);
// Shorten the valid Ble Advertisement.
auto short_ble_advertisement_bytes(
ByteArray(ble_advertisement_bytes.data(),
BleAdvertisement::kMinAdvertisementLength - 1));
auto short_ble_advertisement =
BleAdvertisement(short_ble_advertisement_bytes);
auto is_valid = short_ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
TEST(BleAdvertisementTest,
ConstructionFromByesWithWrongEndpointNameLengthFails) {
// Serialize good data into a good Ble Advertisement.
auto service_id_hash = ByteArray(kServiceIDHashBytes,
sizeof(kServiceIDHashBytes) / sizeof(char));
auto ble_advertisement =
BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID,
kEndpointName, kBluetoothMacAddress);
auto ble_advertisement_bytes = ByteArray(ble_advertisement);
// Corrupt the EndpointNameLength bits.
std::string corrupt_ble_advertisement_string(ble_advertisement_bytes.data(),
ble_advertisement_bytes.size());
corrupt_ble_advertisement_string[8] ^= 0x0FF;
auto corrupt_ble_advertisement_bytes =
ByteArray(corrupt_ble_advertisement_string);
auto corrupt_ble_advertisement =
BleAdvertisement(corrupt_ble_advertisement_bytes);
auto is_valid = corrupt_ble_advertisement.IsValid();
EXPECT_FALSE(is_valid);
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+475
View File
@@ -0,0 +1,475 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/client_proxy.h"
#include <cstdlib>
#include <limits>
#include <utility>
#include "platform_v2/base/base64_utils.h"
#include "platform_v2/base/prng.h"
#include "platform_v2/public/crypto.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/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/str_cat.h"
namespace location {
namespace nearby {
namespace connections {
ClientProxy::ClientProxy() : client_id_(Prng().NextInt64()) {}
ClientProxy::~ClientProxy() { Reset(); }
std::int64_t ClientProxy::GetClientId() const { return client_id_; }
std::string ClientProxy::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.
ByteArray id_hash(Crypto::Sha256(
absl::StrCat(api::ImplementationPlatform::GetDeviceId(), GetClientId())));
return Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength);
}
void ClientProxy::Reset() {
MutexLock lock(&mutex_);
StoppedAdvertising();
StoppedDiscovery();
RemoveAllEndpoints();
}
void ClientProxy::StartedAdvertising(
const std::string& service_id, Strategy strategy,
const ConnectionListener& listener,
absl::Span<proto::connections::Medium> mediums) {
MutexLock lock(&mutex_);
advertising_info_ = {service_id, listener};
}
void ClientProxy::StoppedAdvertising() {
MutexLock lock(&mutex_);
if (IsAdvertising()) {
advertising_info_.Clear();
}
}
bool ClientProxy::IsAdvertising() const {
MutexLock lock(&mutex_);
return !advertising_info_.IsEmpty();
}
std::string ClientProxy::GetAdvertisingServiceId() const {
MutexLock lock(&mutex_);
return advertising_info_.service_id;
}
void ClientProxy::StartedDiscovery(
const std::string& service_id, Strategy strategy,
const DiscoveryListener& listener,
absl::Span<proto::connections::Medium> mediums) {
MutexLock lock(&mutex_);
discovery_info_ = DiscoveryInfo{service_id, listener};
}
void ClientProxy::StoppedDiscovery() {
MutexLock lock(&mutex_);
if (IsDiscovering()) {
discovered_endpoint_ids_.clear();
discovery_info_.Clear();
}
}
bool ClientProxy::IsDiscoveringServiceId(const std::string& service_id) const {
MutexLock lock(&mutex_);
return IsDiscovering() && service_id == discovery_info_.service_id;
}
bool ClientProxy::IsDiscovering() const {
MutexLock lock(&mutex_);
return !discovery_info_.IsEmpty();
}
std::string ClientProxy::GetDiscoveryServiceId() const {
MutexLock lock(&mutex_);
return discovery_info_.service_id;
}
void ClientProxy::OnEndpointFound(const std::string& service_id,
const std::string& endpoint_id,
const std::string& endpoint_name,
proto::connections::Medium medium) {
MutexLock lock(&mutex_);
if (!IsDiscoveringServiceId(service_id)) return;
if (discovered_endpoint_ids_.count(endpoint_id)) {
// TODO(tracyzhou): Add logging.
return;
}
discovered_endpoint_ids_.insert(endpoint_id);
discovery_info_.listener.endpoint_found_cb(endpoint_id, endpoint_name,
service_id);
}
void ClientProxy::OnEndpointLost(const std::string& service_id,
const std::string& endpoint_id) {
MutexLock lock(&mutex_);
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);
}
void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id,
const ConnectionResponseInfo& info,
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.
auto result = connections_.emplace(
endpoint_id, Connection{
.is_incoming = info.is_incoming_connection,
.connection_listener = listener,
});
// 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;
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.
item.connection_listener.initiated_cb(endpoint_id, info);
}
void ClientProxy::OnConnectionAccepted(const std::string& endpoint_id) {
MutexLock lock(&mutex_);
if (!HasPendingConnectionToEndpoint(endpoint_id)) {
// TODO(tracyzhou): Add logging.
return;
}
// Notify the client.
Connection* item = LookupConnection(endpoint_id);
if (item != nullptr) {
item->connection_listener.accepted_cb(endpoint_id);
item->status = Connection::kConnected;
}
}
void ClientProxy::OnConnectionRejected(const std::string& endpoint_id,
const Status& status) {
MutexLock lock(&mutex_);
if (!HasPendingConnectionToEndpoint(endpoint_id)) {
NEARBY_LOG(INFO, "ClientProxy [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 */);
}
}
void ClientProxy::OnBandwidthChanged(const std::string& endpoint_id,
std::int32_t quality) {
MutexLock lock(&mutex_);
const Connection* item = LookupConnection(endpoint_id);
if (item != nullptr) {
item->connection_listener.bandwidth_changed_cb(endpoint_id, quality);
}
}
void ClientProxy::OnDisconnected(const std::string& endpoint_id, bool notify) {
MutexLock lock(&mutex_);
const Connection* item = LookupConnection(endpoint_id);
if (item != nullptr) {
if (notify) {
item->connection_listener.disconnected_cb({endpoint_id});
}
connections_.erase(endpoint_id);
}
}
bool ClientProxy::ConnectionStatusMatches(const std::string& endpoint_id,
Connection::Status status) const {
MutexLock lock(&mutex_);
const Connection* item = LookupConnection(endpoint_id);
if (item != nullptr) {
return item->status == status;
}
return false;
}
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 (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;
}
std::vector<std::string> ClientProxy::GetPendingConnectedEndpoints() const {
return GetMatchingEndpoints([](const Connection& connection) {
return connection.status != Connection::kConnected;
});
}
std::vector<std::string> ClientProxy::GetConnectedEndpoints() const {
return GetMatchingEndpoints([](const Connection& connection) {
return connection.status == Connection::kConnected;
});
}
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)) {
// TODO(tracyzhou): Add logging.
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)) {
// TODO(tracyzhou): Add logging.
return;
}
AppendConnectionStatus(endpoint_id, Connection::kLocalEndpointRejected);
}
void ClientProxy::RemoteEndpointAcceptedConnection(
const std::string& endpoint_id) {
MutexLock lock(&mutex_);
if (HasRemoteEndpointResponded(endpoint_id)) {
// TODO(tracyzhou): Add logging.
return;
}
AppendConnectionStatus(endpoint_id, Connection::kRemoteEndpointAccepted);
}
void ClientProxy::RemoteEndpointRejectedConnection(
const std::string& endpoint_id) {
MutexLock lock(&mutex_);
if (HasRemoteEndpointResponded(endpoint_id)) {
// TODO(tracyzhou): Add logging.
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));
}
}
}
const ClientProxy::Connection* ClientProxy::LookupConnection(
const std::string& endpoint_id) const {
auto item = connections_.find(endpoint_id);
return item != connections_.end() ? &item->second : nullptr;
}
ClientProxy::Connection* ClientProxy::LookupConnection(
const std::string& endpoint_id) {
auto item = connections_.find(endpoint_id);
return item != connections_.end() ? &item->second : nullptr;
}
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);
}
}
}
bool operator==(const ClientProxy& lhs, const ClientProxy& rhs) {
return lhs.GetClientId() == rhs.GetClientId();
}
bool operator<(const ClientProxy& lhs, const ClientProxy& rhs) {
return lhs.GetClientId() < rhs.GetClientId();
}
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.
connections_.clear();
}
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;
}
return false;
}
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);
}
}
} // namespace connections
} // namespace nearby
} // namespace location
+231
View File
@@ -0,0 +1,231 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_CLIENT_PROXY_H_
#define CORE_V2_INTERNAL_CLIENT_PROXY_H_
#include <cstdint>
#include <string>
#include <vector>
#include "core_v2/listeners.h"
#include "core_v2/status.h"
#include "core_v2/strategy.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/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 {
// CLientProxy is tracking state of client's connection, and serves as
// a proxy for notifications sent to this client.
class ClientProxy final {
public:
static constexpr int kEndpointIdLength = 4;
ClientProxy();
~ClientProxy();
ClientProxy(ClientProxy&&) = default;
ClientProxy& operator=(ClientProxy&&) = default;
std::int64_t GetClientId() const;
std::string GenerateLocalEndpointId();
// Clears all the runtime state of this client.
void Reset();
// Marks this client as advertising with the given callbacks.
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() const;
std::string GetAdvertisingServiceId() const;
// Marks this client as discovering with the given callback.
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) const;
bool IsDiscovering() const;
std::string GetDiscoveryServiceId() const;
// Proxies to the client's DiscoveryListener::OnEndpointFound() callback.
void OnEndpointFound(const std::string& service_id,
const std::string& endpoint_id,
const std::string& endpoint_name,
proto::connections::Medium medium);
// 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 ConnectionListener::OnInitiated() callback.
void OnConnectionInitiated(const std::string& endpoint_id,
const ConnectionResponseInfo& info,
const ConnectionListener& listener);
// 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, std::int32_t quality);
// Removes the endpoint from this client's list of connected endpoints. If
// notify is true, also calls the client's
// ConnectionListener.disconnected_cb() callback.
void OnDisconnected(const std::string& endpoint_id, bool notify);
// Returns true if it's safe to send payloads to this endpoint.
bool IsConnectedToEndpoint(const std::string& endpoint_id) const;
// Returns all endpoints that can safely be sent payloads.
std::vector<std::string> GetConnectedEndpoints() const;
// Returns all endpoints that are still awaiting acceptance.
std::vector<std::string> GetPendingConnectedEndpoints() const;
// Returns the number of endpoints that are connected and outgoing.
std::int32_t GetNumOutgoingConnections() const;
// Returns the number of endpoints that are connected and incoming.
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) const;
// Returns true if the local endpoint has already marked itself as
// accepted/rejected.
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) const;
// Marks the local endpoint as having accepted the connection.
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);
// Marks the remote endpoint as having accepted the connection.
void RemoteEndpointAcceptedConnection(const std::string& endpoint_id);
// Marks the remote endpoint as having rejected the connection.
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) const;
// Returns true if either the local endpoint or the remote endpoint has
// rejected the connection.
bool IsConnectionRejected(const std::string& endpoint_id) const;
// 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 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;
};
struct AdvertisingInfo {
std::string service_id;
ConnectionListener listener;
void Clear() { service_id.clear(); }
bool IsEmpty() const { return service_id.empty(); }
};
struct DiscoveryInfo {
std::string service_id;
DiscoveryListener listener;
void Clear() { service_id.clear(); }
bool IsEmpty() const { return service_id.empty(); }
};
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);
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;
mutable RecursiveMutex mutex_;
std::int64_t client_id_;
// If not empty, we are currently advertising and accepting connection
// requests for the given service_id.
AdvertisingInfo advertising_info_;
// If not empty, we are currently discovering for the given service_id.
DiscoveryInfo discovery_info_;
// Maps endpoint_id to endpoint connection state.
absl::flat_hash_map<std::string, Connection> connections_;
// 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
#endif // CORE_V2_INTERNAL_CLIENT_PROXY_H_
+371
View File
@@ -0,0 +1,371 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/client_proxy.h"
#include <string>
#include "core_v2/listeners.h"
#include "core_v2/strategy.h"
#include "platform_v2/base/byte_array.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_set.h"
#include "absl/types/span.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
using ::testing::MockFunction;
using ::testing::StrictMock;
class ClientProxyTest : public testing::Test {
protected:
struct MockDiscoveryListener {
StrictMock<MockFunction<void(const std::string& endpoint_id,
const std::string& endpoint_name,
const std::string& service_id)>>
endpoint_found_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>>
endpoint_lost_cb;
};
struct MockConnectionListener {
StrictMock<MockFunction<void(const std::string& endpoint_id,
const ConnectionResponseInfo& info)>>
initiated_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>> accepted_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
const Status& status)>>
rejected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>>
disconnected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
std::int32_t quality)>>
bandwidth_changed_cb;
};
struct MockPayloadListener {
StrictMock<
MockFunction<void(const std::string& endpoint_id, Payload payload)>>
payload_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
const PayloadProgressInfo& info)>>
payload_progress_cb;
};
struct Endpoint {
std::string name;
std::string id;
};
Endpoint StartAdvertising(ClientProxy* client, ConnectionListener listener) {
Endpoint endpoint{
.name = "advertising endpoint name",
.id = client->GenerateLocalEndpointId(),
};
client->StartedAdvertising(service_id_, strategy_, listener,
absl::MakeSpan(mediums_));
return endpoint;
}
Endpoint StartDiscovery(ClientProxy* client, DiscoveryListener listener) {
Endpoint endpoint{
.name = "discovery endpoint name",
.id = client->GenerateLocalEndpointId(),
};
client->StartedDiscovery(service_id_, strategy_, listener,
absl::MakeSpan(mediums_));
return endpoint;
}
void OnDiscoveryEndpointFound(ClientProxy* client, const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_.endpoint_found_cb, Call).Times(1);
client->OnEndpointFound(service_id_, endpoint.id, endpoint.name, medium_);
}
void OnDiscoveryEndpointLost(ClientProxy* client, const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_.endpoint_lost_cb, Call).Times(1);
client->OnEndpointLost(service_id_, endpoint.id);
}
void OnDiscoveryConnectionInitiated(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_connection_.initiated_cb, Call).Times(1);
const std::string auth_token{"auth_token"};
const ByteArray raw_auth_token{auth_token};
advertising_connection_info_.remote_endpoint_name = endpoint.name;
client->OnConnectionInitiated(endpoint.id, advertising_connection_info_,
discovery_connection_listener_);
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
}
void OnDiscoveryConnectionLocalAccepted(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
EXPECT_FALSE(client->HasLocalEndpointResponded(endpoint.id));
client->LocalEndpointAcceptedConnection(endpoint.id, payload_listener_);
EXPECT_TRUE(client->HasLocalEndpointResponded(endpoint.id));
EXPECT_TRUE(client->LocalConnectionIsAccepted(endpoint.id));
}
void OnDiscoveryConnectionRemoteAccepted(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
EXPECT_FALSE(client->HasRemoteEndpointResponded(endpoint.id));
client->RemoteEndpointAcceptedConnection(endpoint.id);
EXPECT_TRUE(client->HasRemoteEndpointResponded(endpoint.id));
EXPECT_TRUE(client->RemoteConnectionIsAccepted(endpoint.id));
}
void OnDiscoveryConnectionLocalRejected(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
EXPECT_FALSE(client->HasLocalEndpointResponded(endpoint.id));
client->LocalEndpointRejectedConnection(endpoint.id);
EXPECT_TRUE(client->HasLocalEndpointResponded(endpoint.id));
EXPECT_FALSE(client->LocalConnectionIsAccepted(endpoint.id));
}
void OnDiscoveryConnectionRemoteRejected(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
EXPECT_FALSE(client->HasRemoteEndpointResponded(endpoint.id));
client->RemoteEndpointRejectedConnection(endpoint.id);
EXPECT_TRUE(client->HasRemoteEndpointResponded(endpoint.id));
EXPECT_FALSE(client->RemoteConnectionIsAccepted(endpoint.id));
}
void OnDiscoveryConnectionAccepted(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_connection_.accepted_cb, Call).Times(1);
EXPECT_TRUE(client->IsConnectionAccepted(endpoint.id));
client->OnConnectionAccepted(endpoint.id);
}
void OnDiscoveryConnectionRejected(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_connection_.rejected_cb, Call).Times(1);
EXPECT_TRUE(client->IsConnectionRejected(endpoint.id));
client->OnConnectionRejected(endpoint.id, {Status::kConnectionRejected});
}
void OnDiscoveryBandwidthChanged(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_connection_.bandwidth_changed_cb, Call).Times(1);
client->OnBandwidthChanged(endpoint.id, 1);
}
void OnDiscoveryConnectionDisconnected(ClientProxy* client,
const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_connection_.disconnected_cb, Call).Times(1);
client->OnDisconnected(endpoint.id, true);
}
void OnPayload(ClientProxy* client, const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_payload_.payload_cb, Call).Times(1);
client->OnPayload(endpoint.id, Payload(payload_bytes_));
}
void OnPayloadProgress(ClientProxy* client, const Endpoint& endpoint) {
EXPECT_CALL(mock_discovery_payload_.payload_progress_cb, Call).Times(1);
client->OnPayloadProgress(endpoint.id, {});
}
MockDiscoveryListener mock_discovery_;
MockConnectionListener mock_discovery_connection_;
MockPayloadListener mock_discovery_payload_;
proto::connections::Medium medium_{proto::connections::Medium::BLUETOOTH};
std::vector<proto::connections::Medium> mediums_{
proto::connections::Medium::BLUETOOTH,
};
Strategy strategy_{Strategy::kP2pPointToPoint};
const std::string service_id_{"service"};
ClientProxy client1_;
ClientProxy client2_;
std::string auth_token_ = "auth_token";
ByteArray raw_auth_token_ = ByteArray(auth_token_);
ByteArray payload_bytes_{"bytes"};
ConnectionResponseInfo advertising_connection_info_{
.authentication_token = auth_token_,
.raw_authentication_token = raw_auth_token_,
.is_incoming_connection = true,
};
ConnectionListener advertising_connection_listener_;
ConnectionListener discovery_connection_listener_{
.initiated_cb = mock_discovery_connection_.initiated_cb.AsStdFunction(),
.accepted_cb = mock_discovery_connection_.accepted_cb.AsStdFunction(),
.rejected_cb = mock_discovery_connection_.rejected_cb.AsStdFunction(),
.disconnected_cb =
mock_discovery_connection_.disconnected_cb.AsStdFunction(),
.bandwidth_changed_cb =
mock_discovery_connection_.bandwidth_changed_cb.AsStdFunction(),
};
DiscoveryListener discovery_listener_{
.endpoint_found_cb = mock_discovery_.endpoint_found_cb.AsStdFunction(),
.endpoint_lost_cb = mock_discovery_.endpoint_lost_cb.AsStdFunction(),
};
PayloadListener payload_listener_{
.payload_cb = mock_discovery_payload_.payload_cb.AsStdFunction(),
.payload_progress_cb =
mock_discovery_payload_.payload_progress_cb.AsStdFunction(),
};
};
TEST_F(ClientProxyTest, ConstructorDestructorWorks) { SUCCEED(); }
TEST_F(ClientProxyTest, ClientIdIsUnique) {
EXPECT_NE(client1_.GetClientId(), client2_.GetClientId());
}
TEST_F(ClientProxyTest, GeneratedEndpointIdIsUnique) {
EXPECT_NE(client1_.GenerateLocalEndpointId(),
client2_.GenerateLocalEndpointId());
}
TEST_F(ClientProxyTest, ResetClearsState) {
client1_.Reset();
EXPECT_FALSE(client1_.IsAdvertising());
EXPECT_FALSE(client1_.IsDiscovering());
EXPECT_TRUE(client1_.GetAdvertisingServiceId().empty());
EXPECT_TRUE(client1_.GetDiscoveryServiceId().empty());
}
TEST_F(ClientProxyTest, StartedAdvertisingChangesStateFromIdle) {
client1_.StartedAdvertising(service_id_, strategy_, {}, {});
EXPECT_TRUE(client1_.IsAdvertising());
EXPECT_FALSE(client1_.IsDiscovering());
EXPECT_EQ(client1_.GetAdvertisingServiceId(), service_id_);
EXPECT_TRUE(client1_.GetDiscoveryServiceId().empty());
}
TEST_F(ClientProxyTest, StartedDiscoveryChangesStateFromIdle) {
client1_.StartedDiscovery(service_id_, strategy_, {}, {});
EXPECT_FALSE(client1_.IsAdvertising());
EXPECT_TRUE(client1_.IsDiscovering());
EXPECT_TRUE(client1_.GetAdvertisingServiceId().empty());
EXPECT_EQ(client1_.GetDiscoveryServiceId(), service_id_);
}
TEST_F(ClientProxyTest, OnEndpointFoundFiresNotificationInDiscovery) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnEndpointLostFiresNotificationInDiscovery) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryEndpointLost(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnConnectionInitiatedFiresNotificationInDiscovery) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnBandwidthChangedFiresNotificationInDiscovery) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionLocalAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionRemoteAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionAccepted(&client2_, advertising_endpoint);
OnDiscoveryBandwidthChanged(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnDisconnectedFiresNotificationInDiscovery) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionDisconnected(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, LocalEndpointAcceptedConnectionChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionLocalAccepted(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, LocalEndpointRejectedConnectionChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionLocalRejected(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, RemoteEndpointAcceptedConnectionChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionRemoteAccepted(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, RemoteEndpointRejectedConnectionChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionRemoteRejected(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnPayloadChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionLocalAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionRemoteAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionAccepted(&client2_, advertising_endpoint);
OnPayload(&client2_, advertising_endpoint);
}
TEST_F(ClientProxyTest, OnPayloadProgressChangesState) {
Endpoint advertising_endpoint =
StartAdvertising(&client1_, advertising_connection_listener_);
StartDiscovery(&client2_, discovery_listener_);
OnDiscoveryEndpointFound(&client2_, advertising_endpoint);
OnDiscoveryConnectionInitiated(&client2_, advertising_endpoint);
OnDiscoveryConnectionLocalAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionRemoteAccepted(&client2_, advertising_endpoint);
OnDiscoveryConnectionAccepted(&client2_, advertising_endpoint);
OnPayloadProgress(&client2_, advertising_endpoint);
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+382
View File
@@ -0,0 +1,382 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/encryption_runner.h"
#include <cinttypes>
#include <cstdint>
#include <memory>
#include "platform_v2/base/base64_utils.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/public/cancelable_alarm.h"
#include "platform_v2/public/logging.h"
#include "securegcm/ukey2_handshake.h"
#include "absl/strings/ascii.h"
#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-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;
}
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;
}
ByteArray raw_authentication_token(*verification_string);
listener.on_success_cb(endpoint_id, std::move(ukey2),
ToHumanReadableString(raw_authentication_token),
raw_authentication_token);
return true;
}
void CancelableAlarmRunnable(ClientProxy* client_proxy,
const std::string& endpoint_id,
EndpointChannel* endpoint_channel) {
NEARBY_LOG(INFO,
"Timing out encryption for client %" PRId64
" to endpoint %s after %" PRId64 " ms",
client_proxy->GetClientId(), endpoint_id.c_str(),
static_cast<std::int64_t>(absl::ToInt64Milliseconds(kTimeout)));
endpoint_channel->Close();
}
class ServerRunnable final {
public:
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),
channel_(channel),
listener_(std::move(listener)) {}
void operator()() const {
CancelableAlarm timeout_alarm(
"EncryptionRunner.startServer() timeout",
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
kTimeout, alarm_executor_);
std::unique_ptr<securegcm::UKey2Handshake> server =
securegcm::UKey2Handshake::ForResponder(kCipher);
if (server == nullptr) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
// Message 1 (Client Init)
ExceptionOr<ByteArray> client_init = channel_->Read();
if (!client_init.ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
securegcm::UKey2Handshake::ParseResult parse_result =
server->ParseHandshakeMessage(std::string(client_init.result()));
// Java code throws a HandshakeException / AlertException.
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 1 from endpoint %s",
endpoint_id_.c_str());
// Message 2 (Server Init)
std::unique_ptr<std::string> server_init =
server->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (server_init == nullptr) {
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",
endpoint_id_.c_str());
// Message 3 (Client Finish)
ExceptionOr<ByteArray> client_finish = channel_->Read();
if (!client_finish.ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
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();
if (!HandleEncryptionSuccess(endpoint_id_, std::move(server), listener_)) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
}
private:
void LogException() const {
NEARBY_LOG(ERROR, "In startServer(), UKEY2 failed with endpoint %s",
endpoint_id_.c_str());
}
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
timeout_alarm->Cancel();
listener_.on_failure_cb(endpoint_id_, channel_);
}
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());
}
}
ClientProxy* client_;
ScheduledExecutor* alarm_executor_;
const std::string endpoint_id_;
EndpointChannel* channel_;
EncryptionRunner::ResultListener listener_;
};
class ClientRunnable final {
public:
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),
channel_(channel),
listener_(std::move(listener)) {}
void operator()() const {
CancelableAlarm timeout_alarm(
"EncryptionRunner.startClient() timeout",
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
kTimeout, alarm_executor_);
std::unique_ptr<securegcm::UKey2Handshake> crypto =
securegcm::UKey2Handshake::ForInitiator(kCipher);
// Java code throws a HandshakeException.
if (crypto == nullptr) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
// Message 1 (Client Init)
std::unique_ptr<std::string> client_init =
crypto->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (client_init == nullptr) {
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<ByteArray> server_init = channel_->Read();
if (!server_init.ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
securegcm::UKey2Handshake::ParseResult parse_result =
crypto->ParseHandshakeMessage(std::string(server_init.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 startClient(), read UKEY2 Message 2 from endpoint %s",
endpoint_id_.c_str());
// Message 3 (Client Finish)
std::unique_ptr<std::string> client_finish =
crypto->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (client_finish == nullptr) {
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();
if (!HandleEncryptionSuccess(endpoint_id_, std::move(crypto), listener_)) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
}
private:
void LogException() const {
NEARBY_LOG(ERROR, "In startClient(), UKEY2 failed with endpoint %s",
endpoint_id_.c_str());
}
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
timeout_alarm->Cancel();
listener_.on_failure_cb(endpoint_id_, channel_);
}
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());
}
}
ClientProxy* client_;
ScheduledExecutor* alarm_executor_;
const std::string endpoint_id_;
EndpointChannel* channel_;
EncryptionRunner::ResultListener listener_;
};
} // namespace
EncryptionRunner::~EncryptionRunner() {
// Stop all the ongoing Runnables (as gracefully as possible).
client_executor_.Shutdown();
server_executor_.Shutdown();
alarm_executor_.Shutdown();
}
void EncryptionRunner::StartServer(
ClientProxy* client_proxy, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
EncryptionRunner::ResultListener&& listener) {
server_executor_.Execute(
[runnable{ServerRunnable(client_proxy, &alarm_executor_, endpoint_id,
endpoint_channel, std::move(listener))}]() {
runnable();
});
}
void EncryptionRunner::StartClient(
ClientProxy* client_proxy, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
EncryptionRunner::ResultListener&& listener) {
client_executor_.Execute(
[runnable{ClientRunnable(client_proxy, &alarm_executor_, endpoint_id,
endpoint_channel, std::move(listener))}]() {
runnable();
});
}
} // namespace connections
} // namespace nearby
} // namespace location
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_ENCRYPTION_RUNNER_H_
#define CORE_V2_INTERNAL_ENCRYPTION_RUNNER_H_
#include <string>
#include "core_v2/internal/client_proxy.h"
#include "core_v2/internal/endpoint_channel.h"
#include "core_v2/listeners.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/public/scheduled_executor.h"
#include "platform_v2/public/single_thread_executor.h"
#include "securegcm/ukey2_handshake.h"
namespace location {
namespace nearby {
namespace connections {
// Encrypts a connection over UKEY2.
//
// NOTE: Stalled EndpointChannels will be disconnected after kTimeout.
// This is to prevent unverified endpoints from maintaining an
// indefinite connection to us.
class EncryptionRunner {
public:
EncryptionRunner() = default;
~EncryptionRunner();
struct ResultListener {
// @EncryptionRunnerThread
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.
//
// 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
std::function<void(const std::string& endpoint_id,
EndpointChannel* channel)>
on_failure_cb = DefaultCallback<const std::string&, EndpointChannel*>();
};
// @AnyThread
void StartServer(ClientProxy* client_proxy, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
ResultListener&& result_listener);
// @AnyThread
void StartClient(ClientProxy* client_proxy, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
ResultListener&& result_listener);
private:
ScheduledExecutor alarm_executor_;
SingleThreadExecutor server_executor_;
SingleThreadExecutor client_executor_;
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_ENCRYPTION_RUNNER_H_
@@ -0,0 +1,142 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/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 "proto/connections_enums.pb.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
using ::location::nearby::proto::connections::Medium;
class FakeEndpointChannel : public EndpointChannel {
public:
FakeEndpointChannel(InputStream* in, OutputStream* out)
: in_(in), out_(out) {}
ExceptionOr<ByteArray> Read() override {
read_timestamp_ = SystemClock::ElapsedRealtime();
return in_ ? in_->Read(Pipe::kChunkSize)
: ExceptionOr<ByteArray>{Exception::kIo};
}
Exception Write(const ByteArray& data) override {
return out_ ? out_->Write(data) : Exception{Exception::kIo};
}
void Close() override {
if (in_) in_->Close();
if (out_) out_->Close();
}
void Close(proto::connections::DisconnectionReason reason) override {
Close();
}
std::string GetType() const override { return "fake-channel-type"; }
std::string GetName() const override { return "fake-channel"; }
Medium GetMedium() const override { return Medium::BLE; }
void EnableEncryption(
securegcm::D2DConnectionContextV1* connection_context) override {}
bool IsPaused() const override { return false; }
void Pause() override {}
void Resume() override {}
absl::Time GetLastReadTimestamp() const override { return read_timestamp_; }
private:
InputStream* in_ = nullptr;
OutputStream* out_ = nullptr;
absl::Time read_timestamp_ = absl::InfinitePast();
};
struct User {
User(Pipe* reader, Pipe* writer)
: channel(&reader->GetInputStream(), &writer->GetOutputStream()) {}
FakeEndpointChannel channel;
EncryptionRunner crypto;
ClientProxy client;
};
struct Response {
enum class Status {
kUnknown = 0,
kDone = 1,
kFailed = 2,
};
CountDownLatch latch{2};
Status server_status = Status::kUnknown;
Status client_status = Status::kUnknown;
};
TEST(EncryptionRunnerTest, ConstructorDestructorWorks) { EncryptionRunner enc; }
TEST(EncryptionRunnerTest, ReadWrite) {
Pipe from_a_to_b;
Pipe from_b_to_a;
User user_a(/*reader=*/&from_b_to_a, /*writer=*/&from_a_to_b);
User user_b(/*reader=*/&from_a_to_b, /*writer=*/&from_b_to_a);
Response response;
user_a.crypto.StartServer(
&user_a.client, "endpoint_id", &user_a.channel,
{
.on_success_cb =
[&response](const string& endpoint_id,
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
const string& auth_token,
const ByteArray& raw_auth_token) {
response.server_status = Response::Status::kDone;
response.latch.CountDown();
},
.on_failure_cb =
[&response](const string& endpoint_id, EndpointChannel* channel) {
response.server_status = Response::Status::kFailed;
response.latch.CountDown();
},
});
user_b.crypto.StartClient(
&user_b.client, "endpoint_id", &user_b.channel,
{
.on_success_cb =
[&response](const string& endpoint_id,
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
const string& auth_token,
const ByteArray& raw_auth_token) {
response.client_status = Response::Status::kDone;
response.latch.CountDown();
},
.on_failure_cb =
[&response](const string& endpoint_id, EndpointChannel* channel) {
response.client_status = Response::Status::kFailed;
response.latch.CountDown();
},
});
EXPECT_TRUE(response.latch.Await(absl::Milliseconds(5000)).result());
EXPECT_EQ(response.server_status, Response::Status::kDone);
EXPECT_EQ(response.client_status, Response::Status::kDone);
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+88
View File
@@ -0,0 +1,88 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_ENDPOINT_CHANNEL_H_
#define CORE_V2_INTERNAL_ENDPOINT_CHANNEL_H_
#include <cstdint>
#include <string>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "proto/connections_enums.pb.h"
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
class EndpointChannel {
public:
virtual ~EndpointChannel() = default;
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;
// Closes this EndpointChannel and records the closure with the given reason.
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 std::string GetType() const = 0;
// Returns the name of the EndpointChannel.
virtual std::string GetName() const = 0;
// Returns the analytics enum representing the medium of this EndpointChannel.
virtual proto::connections::Medium GetMedium() const = 0;
// Enables encryption on the EndpointChannel.
virtual void EnableEncryption(
securegcm::D2DConnectionContextV1* context) = 0;
// True if the EndpointChannel is currently pausing all writes.
virtual bool IsPaused() const = 0;
// Pauses all writes on this EndpointChannel until resume() is called.
virtual void Pause() = 0;
// Resumes any writes on this EndpointChannel that were suspended when pause()
// was called.
virtual void Resume() = 0;
// Returns the timestamp of the last read from this endpoint, or -1 if no
// reads have occurred.
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
#endif // CORE_V2_INTERNAL_ENDPOINT_CHANNEL_H_
@@ -0,0 +1,151 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/endpoint_channel_manager.h"
#include <memory>
#include "platform_v2/public/logging.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/mutex_lock.h"
namespace location {
namespace nearby {
namespace connections {
EndpointChannelManager::~EndpointChannelManager() {
MutexLock lock(&mutex_);
channel_state_.DestroyAll();
}
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());
}
void EndpointChannelManager::ReplaceChannelForEndpoint(
ClientProxy* client, const std::string& endpoint_id,
std::unique_ptr<EndpointChannel> channel) {
MutexLock lock(&mutex_);
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, endpoint_id, std::move(channel));
}
bool EndpointChannelManager::EncryptChannelForEndpoint(
const std::string& endpoint_id,
std::unique_ptr<EncryptionContext> context) {
MutexLock lock(&mutex_);
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 {};
}
return endpoint->channel;
}
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));
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
if (endpoint->IsEncrypted()) channel_state_.EncryptChannel(endpoint);
}
// 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.get());
return true;
}
return false;
}
///////////////////////////////// ChannelState /////////////////////////////////
EndpointChannelManager::ChannelState::EndpointData*
EndpointChannelManager::ChannelState::LookupEndpointData(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
return item != endpoints_.end() ? &item->second : nullptr;
}
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);
}
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 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;
}
bool EndpointChannelManager::UnregisterChannelForEndpoint(
const std::string& endpoint_id) {
MutexLock lock(&mutex_);
if (!channel_state_.RemoveEndpoint(
endpoint_id,
proto::connections::DisconnectionReason::LOCAL_DISCONNECTION)) {
return false;
}
NEARBY_LOG(INFO, "Unregistered channel: id=%s", endpoint_id.c_str());
return true;
}
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,169 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
#define CORE_V2_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
#include <memory>
#include <string>
#include "core_v2/internal/client_proxy.h"
#include "core_v2/internal/endpoint_channel.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/mutex.h"
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/container/flat_hash_map.h"
namespace location {
namespace nearby {
namespace connections {
using EncryptionContext = ::securegcm::D2DConnectionContextV1;
// 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.
class EndpointChannelManager final {
public:
~EndpointChannelManager();
// 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(ClientProxy* client,
const std::string& endpoint_id,
std::unique_ptr<EndpointChannel> channel)
ABSL_LOCKS_EXCLUDED(mutex_);
// 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):
//
// 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 std::string& endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
// Tracks channel state for all endpoints. This includes what EndpointChannel
// the endpoint is currently using and whether or not the EndpointChannel has
// been encrypted yet.
class ChannelState {
public:
struct EndpointData {
EndpointData() = default;
EndpointData(EndpointData&&) = default;
EndpointData& operator=(EndpointData&&) = default;
~EndpointData() {
if (channel != nullptr) {
channel->Close(disconnect_reason);
}
}
// True if we have a 'context' for the endpoint.
bool IsEncrypted() const { return context != nullptr; }
std::shared_ptr<EndpointChannel> channel;
std::unique_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 std::string& endpoint_id,
proto::connections::DisconnectionReason reason);
bool EncryptChannel(EndpointData* endpoint);
private:
// Endpoint ID -> EndpointData. Contains everything we know about the
// endpoint.
absl::flat_hash_map<std::string, EndpointData> endpoints_;
};
void SetActiveEndpointChannel(ClientProxy* client,
const std::string& endpoint_id,
std::unique_ptr<EndpointChannel> channel)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Mutex mutex_;
ChannelState channel_state_ ABSL_GUARDED_BY(mutex_);
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
@@ -0,0 +1,31 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/endpoint_channel_manager.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
TEST(EndpointChannelManagerTest, ConstructorDestructorWorks) {
EndpointChannelManager mgr;
SUCCEED();
}
} // namespace connections
} // namespace nearby
} // namespace location
+491
View File
@@ -0,0 +1,491 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/endpoint_manager.h"
#include <memory>
#include <utility>
#include "core_v2/internal/endpoint_channel.h"
#include "core_v2/internal/offline_frames.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/logging.h"
#include "proto/connections_enums.pb.h"
namespace location {
namespace nearby {
namespace connections {
using ::location::nearby::proto::connections::Medium;
// A Runnable that continuously grabs the most recent EndpointChannel available
// for an endpoint.
//
// handler - Called whenever an EndpointChannel is available for endpointId.
// Implementations are expected to read/write freely to the
// EndpointChannel until an Exception::IO is thrown. Once an
// Exception::IO occurs, a check will be performed to see if another
// EndpointChannel is available for the given endpoint and, if so,
// handler(EndpointChannel) will be called again. Return false to exit
// the loop.
void EndpointManager::EndpointChannelLoopRunnable(
const std::string& runnable_name, ClientProxy* client,
const std::string& endpoint_id, CountDownLatch* barrier,
std::function<ExceptionOr<bool>(EndpointChannel*)> handler) {
// EndpointChannelManager will not let multiple channels exist simultaneously
// for the same endpoint_id; it will be closing "old" channels as new ones
// come. (There will be a short overlap).
// Closed channel will return Exception::kIo for any Read, and loop (below)
// will retry and attempt to pick another channel.
// If channel is deleted (no mapping), or it is still the same channel
// (same Medium) on which we got the Exception::kIo, we terminate the loop.
Medium last_failed_medium = Medium::UNKNOWN_MEDIUM;
while (true) {
// It's important to keep re-fetching the EndpointChannel for an endpoint
// because it can be changed out from under us (for example, when we
// upgrade from Bluetooth to Wifi).
std::shared_ptr<EndpointChannel> channel =
channel_manager_->GetChannelForEndpoint(endpoint_id);
if (channel == nullptr) {
// TODO(tracyzhou): Add logging.
break;
}
// If we're looping back around after a failure, and there's not a new
// EndpointChannel for this endpoint, there's nothing more to do here.
if ((last_failed_medium != Medium::UNKNOWN_MEDIUM) &&
(channel->GetMedium() == last_failed_medium)) {
// TODO(tracyzhou): Add logging.
break;
}
ExceptionOr<bool> keep_using_channel = handler(channel.get());
if (!keep_using_channel.ok()) {
Exception exception = keep_using_channel.GetException();
if (exception.Raised(Exception::kIo)) {
last_failed_medium = channel->GetMedium();
// TODO(tracyzhou): Add logging.
continue;
}
if (exception.Raised(Exception::kInterrupted)) {
break;
}
}
if (!keep_using_channel.result()) {
// TODO(tracyzhou): Add logging.
break;
}
}
// Indicate we're out of the loop and it is ok to schedule another instance
// if needed.
NEARBY_LOG(INFO, "Worker going down; name=%s; id=%s", runnable_name.c_str(),
endpoint_id.c_str());
barrier->CountDown();
// Always clear out all state related to this endpoint before terminating
// this thread.
DiscardEndpoint(client, endpoint_id);
NEARBY_LOG(INFO, "Worker done; name=%s; id=%s", runnable_name.c_str(),
endpoint_id.c_str());
}
ExceptionOr<bool> EndpointManager::HandleData(
const std::string& endpoint_id, ClientProxy* client,
EndpointChannel* endpoint_channel) {
// Read as much as we can from the healthy EndpointChannel - when it is no
// longer in good shape (i.e. our read from it throws an Exception), our
// super class will loop back around and try our luck in case there's been
// a replacement for this endpoint since we last checked with the
// EndpointChannelManager.
while (true) {
ExceptionOr<ByteArray> bytes = endpoint_channel->Read();
if (!bytes.ok()) {
NEARBY_LOG(INFO, "Stop reading on read-time exception: %d",
bytes.exception());
return ExceptionOr<bool>(bytes.exception());
}
ExceptionOr<OfflineFrame> wrapped_frame = parser::FromBytes(bytes.result());
if (!wrapped_frame.ok()) {
if (wrapped_frame.GetException().Raised(
Exception::kInvalidProtocolBuffer)) {
NEARBY_LOG(INFO, "failed to decode; endpoint=%s; channel=%s; skip",
endpoint_id.c_str(), endpoint_channel->GetType().c_str());
continue;
} else {
NEARBY_LOG(INFO, "Stop reading on parse-time exception: %d",
wrapped_frame.exception());
return ExceptionOr<bool>(wrapped_frame.exception());
}
}
OfflineFrame& frame = wrapped_frame.result();
// Route the incoming offlineFrame to its registered processor.
V1Frame::FrameType frame_type = parser::GetFrameType(frame);
EndpointManager::FrameProcessor* frame_processor =
GetFrameProcessor(frame_type);
if (frame_processor == nullptr) {
NEARBY_LOG(ERROR, "Unhandled message: type=%d", frame_type);
continue;
}
frame_processor->OnIncomingFrame(frame, endpoint_id, client,
endpoint_channel->GetMedium());
}
}
ExceptionOr<bool> EndpointManager::HandleKeepAlive(
EndpointChannel* endpoint_channel) {
// Check if it has been too long since we received a frame from our
// endpoint.
if ((endpoint_channel->GetLastReadTimestamp() != kInvalidTimestamp) &&
((endpoint_channel->GetLastReadTimestamp() +
EndpointManager::kKeepAliveReadTimeout) <
SystemClock::ElapsedRealtime())) {
// TODO(tracyzhou): Add logging.
return ExceptionOr<bool>(false);
}
// Attempt to send the KeepAlive frame over the endpoint channel - if the
// write fails, our super class will loop back around and try our luck again
// in case there's been a replacement for this endpoint.
Exception write_exception = endpoint_channel->Write(parser::ForKeepAlive());
if (!write_exception.Ok()) {
return ExceptionOr<bool>(write_exception);
}
// We sleep as the very last step because we want to minimize the caching of
// the EndpointChannel. If we do hold on to the EndpointChannel, and it's
// switched out from under us in BandwidthUpgradeManager, our write will
// trigger an erroneous write to the encryption context that will cascade
// into all our remote endpoint's future reads failing.
Exception sleep_exception =
SystemClock::Sleep(EndpointManager::kKeepAliveWriteInterval);
if (!sleep_exception.Ok()) {
return ExceptionOr<bool>(sleep_exception);
}
return ExceptionOr<bool>(true);
}
bool operator==(const EndpointManager::FrameProcessor& lhs,
const EndpointManager::FrameProcessor& rhs) {
// We're comparing addresses because these objects are callbacks which need to
// be matched by exact instances.
return &lhs == &rhs;
}
bool operator<(const EndpointManager::FrameProcessor& lhs,
const EndpointManager::FrameProcessor& rhs) {
// We're comparing addresses because these objects are callbacks which need to
// be matched by exact instances.
return &lhs < &rhs;
}
EndpointManager::EndpointManager(EndpointChannelManager* manager)
: channel_manager_(manager) {}
EndpointManager::~EndpointManager() {
CountDownLatch latch(1);
RunOnEndpointManagerThread([this, &latch]() {
NEARBY_LOG(INFO, "Bringing down endpoints");
for (auto& item : endpoints_) {
const std::string& endpoint_id = item.first;
EndpointState& state = item.second;
// This will close the channel; all workers will sense that and
// terminate.
NEARBY_LOG(INFO, "Bringing down endpoint channels: id=%s",
endpoint_id.c_str());
WaitForEndpointDisconnectionProcessing(state.client, endpoint_id);
channel_manager_->UnregisterChannelForEndpoint(endpoint_id);
}
latch.CountDown();
});
latch.Await();
NEARBY_LOG(INFO, "Bringing down worker threads");
// Stop all the ongoing Runnables (as gracefully as possible).
// Order matters: bring worker pools down first; serial_executor_ thread
// should go last, since workers schedule jobs there even during shutdown.
handlers_executor_.Shutdown();
keep_alive_executor_.Shutdown();
NEARBY_LOG(INFO, "Bringing down control thread");
serial_executor_.Shutdown();
NEARBY_LOG(INFO, "EndpointManager is down");
}
const EndpointManager::FrameProcessor::Handle
EndpointManager::RegisterFrameProcessor(
V1Frame::FrameType frame_type, EndpointManager::FrameProcessor* processor) {
const FrameProcessor::Handle handle = processor;
CountDownLatch latch(1);
RunOnEndpointManagerThread([this, frame_type, &latch, processor]() {
auto it = frame_processors_.find(frame_type);
if (it != frame_processors_.end()) {
// TODO(tracyzhou): Add logging.
it->second = processor;
} else {
frame_processors_.emplace(frame_type, processor);
}
latch.CountDown();
});
latch.Await();
return handle;
}
void EndpointManager::UnregisterFrameProcessor(V1Frame::FrameType frame_type,
const void* handle) {
RunOnEndpointManagerThread([this, frame_type, handle]() {
auto it = frame_processors_.find(frame_type);
if (it == frame_processors_.end()) return;
if (it->second != handle) {
NEARBY_LOG(INFO,
"Failed to unregister: type=%d; handle mismatch: passed=%p, "
"expected=%p",
frame_type, handle, it->second);
return;
}
frame_processors_.erase(it);
NEARBY_LOG(INFO, "unregistered: type=%d", frame_type);
});
}
EndpointManager::FrameProcessor* EndpointManager::GetFrameProcessor(
V1Frame::FrameType frame_type) {
EndpointManager::FrameProcessor* processor = nullptr;
CountDownLatch latch(1);
RunOnEndpointManagerThread([this, frame_type, &processor, &latch]() {
auto it = frame_processors_.find(frame_type);
if (it != frame_processors_.end()) {
processor = it->second;
}
latch.CountDown();
});
latch.Await();
return processor;
}
void EndpointManager::EnsureWorkersTerminated(const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
if (item != endpoints_.end()) {
// If another instance of data and keep-alive handlers is running, it will
// terminate soon; we should block until it happens.
EndpointState& endpoint_state = item->second;
NEARBY_LOG(INFO, "Waiting for workers to terminate for endpoint_id='%s'",
endpoint_id.c_str());
endpoint_state.barrier.Await();
endpoints_.erase(item);
}
}
void EndpointManager::RegisterEndpoint(ClientProxy* client,
const std::string& endpoint_id,
const ConnectionResponseInfo& info,
std::unique_ptr<EndpointChannel> channel,
const ConnectionListener& listener) {
CountDownLatch latch(1);
// NOTE (unique_ptr<> capture):
// std::unique_ptr<> is not copyable, so we can not pass it to
// lambda capture, because lambda eventually is converted to std::function<>.
// Instead, we release() a pointer, and pass a raw pointer, which is copyalbe.
// We ignore the risk of job not scheduled (and an associated risk of memory
// leak), because this may only happen during service shutdown.
RunOnEndpointManagerThread([this, client, channel = channel.release(),
&endpoint_id, &info, &listener, &latch]() {
// Pass ownership of channel to EndpointChannelManager
NEARBY_LOG(INFO, "Registering endpoint with channel manager: id=%s",
endpoint_id.c_str());
channel_manager_->RegisterChannelForEndpoint(
client, endpoint_id, std::unique_ptr<EndpointChannel>(channel));
EnsureWorkersTerminated(endpoint_id);
EndpointState& endpoint_state =
endpoints_.emplace(endpoint_id, EndpointState()).first->second;
endpoint_state.client = client;
NEARBY_LOG(INFO, "Starting workers: id=%s", endpoint_id.c_str());
// For every endpoint, there's normally only one Read handler instance
// running on the handlers_executor_ pool. This instance reads data from the
// endpoint and delegates incoming frames to various FrameProcessors.
// Once the frame has been properly handled, it starts reading again for
// the next frame. If the handler fails its read and no other
// EndpointChannels are available for this endpoint, a disconnection
// will be initiated.
StartEndpointReader(
[this, client, endpoint_id, barrier = &endpoint_state.barrier]() {
EndpointChannelLoopRunnable(
"Read", client, endpoint_id, barrier,
[this, client, endpoint_id](EndpointChannel* channel) {
return HandleData(endpoint_id, client, channel);
});
});
// For every endpoint, there's only one KeepAliveManager instance
// running on the keep_alive_executor_ pool. This instance will
// periodically send out a ping* to the endpoint while listening for an
// incoming pong**. If it fails to send the ping, or if no pong is heard
// within kKeepAliveReadTimeoutMillis milliseconds, it initiates a
// disconnection.
//
// (*) Bluetooth requires a constant outgoing stream of messages. If
// there's silence, Android will break the socket. This is why we ping.
// (**) Wifi Hotspots can fail to notice a connection has been lost, and
// they will happily keep writing to /dev/null. This is why we listen
// for the pong.
StartEndpointKeepAliveManager([this, client, endpoint_id,
barrier = &endpoint_state.barrier]() {
EndpointChannelLoopRunnable("KeepAliveManager", client, endpoint_id,
barrier, [this](EndpointChannel* channel) {
return HandleKeepAlive(channel);
});
});
// TODO(tracyzhou): Add logging.
// It's now time to let the client know of this new connection so that
// they can accept or reject it.
client->OnConnectionInitiated(endpoint_id, info, listener);
latch.CountDown();
});
latch.Await();
}
void EndpointManager::UnregisterEndpoint(ClientProxy* client,
const std::string& endpoint_id) {
CountDownLatch latch(1);
RunOnEndpointManagerThread([this, client, endpoint_id, &latch]() {
channel_manager_->UnregisterChannelForEndpoint(endpoint_id);
RemoveEndpoint(client, endpoint_id, /*notify=*/false);
latch.CountDown();
});
latch.Await();
}
// Designed to run asynchronously. It is called from IO thread pools, and
// jobs in these pools may be waited for from the EndpointManager thread. If we
// allow synchronous behavior here it will cause a live lock.
void EndpointManager::DiscardEndpoint(ClientProxy* client,
const std::string& endpoint_id) {
RunOnEndpointManagerThread([this, client, endpoint_id]() {
channel_manager_->UnregisterChannelForEndpoint(endpoint_id);
RemoveEndpoint(client, endpoint_id,
/*notify=*/
client->IsConnectedToEndpoint(endpoint_id));
});
}
std::vector<std::string> EndpointManager::SendPayloadChunk(
const PayloadTransferFrame::PayloadHeader& payload_header,
const PayloadTransferFrame::PayloadChunk& payload_chunk,
const std::vector<std::string>& endpoint_ids) {
ByteArray bytes =
parser::ForDataPayloadTransfer(payload_header, payload_chunk);
return SendTransferFrameBytes(endpoint_ids, bytes, payload_header.id(),
/*offset=*/payload_chunk.offset(),
/*packet_type=*/"DATA");
}
std::vector<std::string> EndpointManager::SendControlMessage(
const PayloadTransferFrame::PayloadHeader& header,
const PayloadTransferFrame::ControlMessage& control,
const std::vector<std::string>& endpoint_ids) {
ByteArray bytes = parser::ForControlPayloadTransfer(header, control);
return SendTransferFrameBytes(endpoint_ids, bytes, header.id(),
/*offset=*/control.offset(),
/*packet_type=*/"CONTROL");
}
// @EndpointManagerThread
void EndpointManager::RemoveEndpoint(ClientProxy* client,
const std::string& endpoint_id,
bool notify) {
// Unregistering from channel_manager_ will also serve to terminate
// the dedicated handler and KeepAlive threads we started when we registered
// this endpoint.
if (channel_manager_->UnregisterChannelForEndpoint(endpoint_id)) {
// Notify all frame processors of the disconnection immediately and wait
// for them to clean up state. Only once all processors are done cleaning
// up, we can remove the endpoint from ClientProxy after which there
// should be no further interactions with the endpoint.
// (See b/37352254 for history)
WaitForEndpointDisconnectionProcessing(client, endpoint_id);
EnsureWorkersTerminated(endpoint_id);
client->OnDisconnected(endpoint_id, notify);
// TODO(tracyzhou): Add logging.
}
}
// @EndpointManagerThread
void EndpointManager::WaitForEndpointDisconnectionProcessing(
ClientProxy* client, const std::string& endpoint_id) {
CountDownLatch barrier(frame_processors_.size());
for (auto& item : frame_processors_) {
auto& processor = item.second;
processor->OnEndpointDisconnect(client, endpoint_id, &barrier);
}
barrier.Await(kProcessEndpointDisconnectionTimeout);
}
std::vector<std::string> EndpointManager::SendTransferFrameBytes(
const std::vector<std::string>& endpoint_ids, const ByteArray& bytes,
std::int64_t payload_id, std::int64_t offset,
const std::string& packet_type) {
std::vector<std::string> failed_endpoint_ids;
for (const std::string& endpoint_id : endpoint_ids) {
std::shared_ptr<EndpointChannel> channel =
channel_manager_->GetChannelForEndpoint(endpoint_id);
if (channel == nullptr) {
// We no longer know about this endpoint (it was either explicitly
// unregistered, or a read/write error made us unregister it internally).
NEARBY_LOG(INFO, "Channel not available; id=%s", endpoint_id.c_str());
failed_endpoint_ids.push_back(endpoint_id);
continue;
}
Exception write_exception = channel->Write(bytes);
if (!write_exception.Ok()) {
failed_endpoint_ids.push_back(endpoint_id);
NEARBY_LOG(INFO, "Failed to send packet; endpoint_id=%s",
endpoint_id.c_str());
continue;
}
}
return failed_endpoint_ids;
}
void EndpointManager::StartEndpointReader(Runnable runnable) {
handlers_executor_.Execute(std::move(runnable));
}
void EndpointManager::StartEndpointKeepAliveManager(Runnable runnable) {
keep_alive_executor_.Execute(std::move(runnable));
}
void EndpointManager::RunOnEndpointManagerThread(Runnable runnable) {
serial_executor_.Execute(std::move(runnable));
}
} // namespace connections
} // namespace nearby
} // namespace location
+232
View File
@@ -0,0 +1,232 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_ENDPOINT_MANAGER_H_
#define CORE_V2_INTERNAL_ENDPOINT_MANAGER_H_
#include <cstdint>
#include <memory>
#include "core_v2/internal/client_proxy.h"
#include "core_v2/internal/endpoint_channel.h"
#include "core_v2/internal/endpoint_channel_manager.h"
#include "core_v2/listeners.h"
#include "proto/connections/offline_wire_formats.pb.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/runnable.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/multi_thread_executor.h"
#include "platform_v2/public/single_thread_executor.h"
#include "platform_v2/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 {
// Manages all operations related to the remote endpoints with which we are
// interacting.
//
// All processing of incoming and outgoing payloads is spread across this and
// the PayloadManager as described below.
//
// 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 Payload::Type.
//
// 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::ProcessFrame() (still running on that
// same dedicated reader thread).
class EndpointManager {
public:
class FrameProcessor {
public:
using Handle = void*;
virtual ~FrameProcessor() = default;
// @EndpointManagerReaderThread
virtual void OnIncomingFrame(const OfflineFrame& offline_frame,
const std::string& from_endpoint_id,
ClientProxy* to_client,
proto::connections::Medium current_medium) = 0;
// Implementations must call barrier.CountDown() once
// they're done. This parallelizes the disconnection event across all frame
// processors.
//
// @EndpointManagerThread
virtual void OnEndpointDisconnect(ClientProxy* client,
const std::string& endpoint_id,
CountDownLatch* barrier) = 0;
};
explicit EndpointManager(EndpointChannelManager* manager);
~EndpointManager();
// Invoked from the constructors of the various *Manager components that make
// up the OfflineServiceController implementation.
// FrameProcessor* instances are of dynamic duration and survive all sessions.
// returns unique handle to be used for unregistering.
// Blocks until registration is complete.
const FrameProcessor::Handle RegisterFrameProcessor(
V1Frame::FrameType frame_type, FrameProcessor* processor);
void UnregisterFrameProcessor(V1Frame::FrameType frame_type,
const void* handle);
// Invoked from the different PcpHandler implementations (of which there can
// be only one at a time).
// Blocks until registration is complete.
void RegisterEndpoint(ClientProxy* client, const std::string& endpoint_id,
const ConnectionResponseInfo& info,
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(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.
// b) We failed to write to the endpoint in PayloadManager.
// c) The connection was rejected in PCPHandler.
// d) The dedicated KeepAlive thread exceeded its period of inactivity.
// Or in the numerous other cases where a failure occurred and we no longer
// 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 FrameProcessor to
// processEndpointDisconnection() while the caller of DiscardEndpoint() is
// blocked here.
void DiscardEndpoint(ClientProxy* client, const std::string& endpoint_id);
private:
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};
};
FrameProcessor* GetFrameProcessor(V1Frame::FrameType frame_type);
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.
// @EndpointManagerThread
void RemoveEndpoint(ClientProxy* client, const std::string& endpoint_id,
bool notify);
void WaitForEndpointDisconnectionProcessing(ClientProxy* client,
const std::string& endpoint_id);
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);
// 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);
// Executes keep-alive jobs on a separate thread for each endpoint on a
// keep_alive_executor_.
void StartEndpointKeepAliveManager(Runnable runnable);
// Executes all jobs sequentially, on a serial_executor_.
void RunOnEndpointManagerThread(Runnable runnable);
EndpointChannelManager* channel_manager_;
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
#endif // CORE_V2_INTERNAL_ENDPOINT_MANAGER_H_
@@ -0,0 +1,256 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/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 "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 "proto/connections_enums.pb.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.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 ::securegcm::D2DConnectionContextV1;
using ::testing::_;
using ::testing::MockFunction;
using ::testing::Return;
using ::testing::StrictMock;
class MockEndpointChannel : public EndpointChannel {
public:
MOCK_METHOD(ExceptionOr<ByteArray>, Read, (), (override));
MOCK_METHOD(Exception, Write, (const ByteArray& data), (override));
MOCK_METHOD(void, Close, (), (override));
MOCK_METHOD(void, Close, (DisconnectionReason reason), (override));
MOCK_METHOD(std::string, GetType, (), (const override));
MOCK_METHOD(std::string, GetName, (), (const override));
MOCK_METHOD(Medium, GetMedium, (), (const override));
MOCK_METHOD(void, EnableEncryption,
(D2DConnectionContextV1 * connection_context),
(override));
MOCK_METHOD(bool, IsPaused, (), (const override));
MOCK_METHOD(void, Pause, (), (override));
MOCK_METHOD(void, Resume, (), (override));
MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const override));
bool IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
void DoClose() {
absl::MutexLock lock(&mutex_);
closed_ = true;
}
private:
mutable absl::Mutex mutex_;
bool closed_ = false;
};
class MockFrameProcessor : public EndpointManager::FrameProcessor {
public:
MOCK_METHOD(void, OnIncomingFrame,
(const OfflineFrame& offline_frame,
const std::string& from_endpoint_id, ClientProxy* to_client,
Medium current_medium),
(override));
MOCK_METHOD(void, OnEndpointDisconnect,
(ClientProxy * client, const std::string& endpoint_id,
CountDownLatch* barrier),
(override));
};
class EndpointManagerTest : public ::testing::Test {
protected:
void RegisterEndpoint(std::unique_ptr<MockEndpointChannel> channel,
bool should_close = true) {
CountDownLatch done(1);
if (should_close) {
ON_CALL(*channel, Close(_))
.WillByDefault(
[&done](DisconnectionReason reason) { done.CountDown(); });
}
EXPECT_CALL(*channel, GetMedium()).WillRepeatedly(Return(Medium::BLE));
EXPECT_CALL(*channel, GetLastReadTimestamp())
.WillRepeatedly(Return(start_time_));
EXPECT_CALL(mock_listener_.initiated_cb, Call).Times(1);
em_.RegisterEndpoint(&client_, endpoint_id_, info_, std::move(channel),
listener_);
if (should_close) {
EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result());
}
}
ClientProxy client_;
std::vector<std::unique_ptr<EndpointManager::FrameProcessor>> processors_;
EndpointChannelManager ecm_;
EndpointManager em_{&ecm_};
std::string endpoint_id_ = "endpoint_id";
ConnectionResponseInfo info_ = {
.remote_endpoint_name = "name",
.authentication_token = "auth_token",
.raw_authentication_token = ByteArray("auth_token"),
.is_incoming_connection = true,
};
struct MockConnectionListener {
StrictMock<MockFunction<void(const std::string& endpoint_id,
const ConnectionResponseInfo& info)>>
initiated_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>> accepted_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
const Status& status)>>
rejected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id)>>
disconnected_cb;
StrictMock<MockFunction<void(const std::string& endpoint_id,
std::int32_t quality)>>
bandwidth_changed_cb;
} mock_listener_;
ConnectionListener listener_{
.initiated_cb = mock_listener_.initiated_cb.AsStdFunction(),
.accepted_cb = mock_listener_.accepted_cb.AsStdFunction(),
.rejected_cb = mock_listener_.rejected_cb.AsStdFunction(),
.disconnected_cb = mock_listener_.disconnected_cb.AsStdFunction(),
.bandwidth_changed_cb =
mock_listener_.bandwidth_changed_cb.AsStdFunction(),
};
absl::Time start_time_{absl::Now()};
};
TEST_F(EndpointManagerTest, ConstructorDestructorWorks) { SUCCEED(); }
TEST_F(EndpointManagerTest, RegisterEndpointCallsOnConnectionInitiated) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
EXPECT_CALL(*endpoint_channel, Read())
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Close(_)).Times(1);
RegisterEndpoint(std::move(endpoint_channel));
}
TEST_F(EndpointManagerTest, UnregisterEndpointCallsOnDisconnected) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
EXPECT_CALL(*endpoint_channel, Read())
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
RegisterEndpoint(std::make_unique<MockEndpointChannel>());
// NOTE: disconnect_cb is not called, because we did not reach fully connected
// state. On top of that, UnregisterEndpoint is suppressing this notification.
// (IMO, it should be called as long as any connection callback was called
// before. (in this case initiated_cb is called)).
// Test captures current protocol behavior.
em_.UnregisterEndpoint(&client_, endpoint_id_);
}
TEST_F(EndpointManagerTest, RegisterFrameProcessorWorks) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
auto connect_request = std::make_unique<MockFrameProcessor>();
auto read_data = parser::ForConnectionRequest("endpoint_id", "endpoint_name",
1234, std::vector{Medium::BLE});
EXPECT_CALL(*connect_request, OnIncomingFrame);
EXPECT_CALL(*connect_request, OnEndpointDisconnect);
EXPECT_CALL(*endpoint_channel, Read())
.WillOnce(Return(ExceptionOr<ByteArray>(read_data)))
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Write(_))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
// Register frame processor, then register endpoint.
// Endpoint will read one frame, then fail to read more and terminate.
// On disconnection, it will notify frame processor and we verify that.
const void* handle = em_.RegisterFrameProcessor(V1Frame::CONNECTION_REQUEST,
connect_request.get());
processors_.emplace_back(std::move(connect_request));
EXPECT_NE(handle, nullptr);
RegisterEndpoint(std::move(endpoint_channel));
}
TEST_F(EndpointManagerTest, UnregisterFrameProcessorWorks) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
EXPECT_CALL(*endpoint_channel, Read())
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Write(_))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
// We should not receive any notifications to frame processor.
auto connect_request = std::make_unique<StrictMock<MockFrameProcessor>>();
// Register frame processor and immediately unregister it.
const void* handle = em_.RegisterFrameProcessor(V1Frame::CONNECTION_REQUEST,
connect_request.get());
processors_.emplace_back(std::move(connect_request));
EXPECT_NE(handle, nullptr);
em_.UnregisterFrameProcessor(V1Frame::CONNECTION_REQUEST, handle);
// Endpoint will not send OnDisconnect notification to frame processor.
RegisterEndpoint(std::move(endpoint_channel), false);
em_.UnregisterEndpoint(&client_, endpoint_id_);
}
TEST_F(EndpointManagerTest, SendControlMessageWorks) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::ControlMessage control;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::BYTES);
header.set_total_size(1024);
control.set_offset(150);
control.set_event(PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED);
ON_CALL(*endpoint_channel, Read())
.WillByDefault([channel = endpoint_channel.get()]() {
if (channel->IsClosed()) return ExceptionOr<ByteArray>(Exception::kIo);
NEARBY_LOG(INFO, "Simulate read delay: wait");
absl::SleepFor(absl::Milliseconds(100));
NEARBY_LOG(INFO, "Simulate read delay: done");
if (channel->IsClosed()) return ExceptionOr<ByteArray>(Exception::kIo);
return ExceptionOr<ByteArray>(ByteArray{});
});
ON_CALL(*endpoint_channel, Close(_))
.WillByDefault(
[channel = endpoint_channel.get()](DisconnectionReason reason) {
channel->DoClose();
NEARBY_LOG(INFO, "Channel closed");
});
EXPECT_CALL(*endpoint_channel, Write(_))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
RegisterEndpoint(std::move(endpoint_channel), false);
auto failed_ids =
em_.SendControlMessage(header, control, std::vector{endpoint_id_});
EXPECT_EQ(failed_ids, std::vector<std::string>{});
NEARBY_LOG(INFO, "Will unregister endpoint now");
em_.UnregisterEndpoint(&client_, endpoint_id_);
NEARBY_LOG(INFO, "Will call destructors now");
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location
+84
View File
@@ -0,0 +1,84 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cc_library(
name = "mediums",
srcs = [
"advertisement_read_result.cc",
"ble_advertisement.cc",
"ble_advertisement_header.cc",
"ble_packet.cc",
"bluetooth_radio.cc",
"uuid.cc",
],
hdrs = [
"advertisement_read_result.h",
"ble_advertisement.h",
"ble_advertisement_header.h",
"ble_packet.h",
"ble_peripheral.h",
"bluetooth_radio.h",
"lost_entity_tracker.h",
"uuid.h",
],
visibility = [
"//core_v2/internal:__pkg__",
],
deps = [
"//platform_v2/base",
"//platform_v2/public",
"//platform_v2/public:logging",
"//absl/container:flat_hash_map",
"//absl/container:flat_hash_set",
"//absl/strings",
"//absl/time",
],
)
cc_library(
name = "utils",
srcs = ["utils.cc"],
hdrs = ["utils.h"],
visibility = [
"//core_v2/internal/mediums/webrtc:__pkg__",
],
deps = [
"//platform_v2/base",
"//platform_v2/public",
],
)
cc_test(
name = "core_v2_internal_mediums_test",
srcs = [
"advertisement_read_result_test.cc",
"ble_advertisement_header_test.cc",
"ble_advertisement_test.cc",
"ble_packet_test.cc",
"ble_peripheral_test.cc",
"bluetooth_radio_test.cc",
"lost_entity_tracker_test.cc",
"uuid_test.cc",
],
shard_count = 16,
deps = [
":mediums",
"//platform_v2/base",
"//platform_v2/impl/g3", # build_cleaner: keep
"//platform_v2/public",
"//platform_v2/public:logging",
"//testing/base/public:gunit_main",
"//absl/time",
],
)
@@ -0,0 +1,139 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/mediums/advertisement_read_result.h"
#include <algorithm>
#include <vector>
#include "platform_v2/public/mutex_lock.h"
#include "absl/container/flat_hash_set.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
const AdvertisementReadResult::Config AdvertisementReadResult::kDefaultConfig{
.backoff_multiplier = 2.0,
.base_backoff_duration = absl::Seconds(1),
.max_backoff_duration = absl::Minutes(5),
};
// Adds a successfully read advertisement for the specified slot to this read
// result. This is fundamentally different from RecordLastReadStatus() because
// we can report a read failure, but still manage to read some advertisements.
void AdvertisementReadResult::AddAdvertisement(std::int32_t slot,
const ByteArray& advertisement) {
MutexLock lock(&mutex_);
// Blindly remove from the advertisements map to make sure any existing
// key-value pair is destroyed.
advertisements_.emplace(slot, advertisement);
}
// Determines whether or not an advertisement was successfully read at the
// specified slot.
bool AdvertisementReadResult::HasAdvertisement(std::int32_t slot) const {
MutexLock lock(&mutex_);
return advertisements_.contains(slot);
}
// Retrieves all raw advertisements that were successfully read.
std::vector<const ByteArray*> AdvertisementReadResult::GetAdvertisements()
const {
MutexLock lock(&mutex_);
std::vector<const ByteArray*> all_advertisements;
all_advertisements.reserve(advertisements_.size());
for (const auto& item : advertisements_) {
all_advertisements.emplace_back(&item.second);
}
return all_advertisements;
}
// Determines what stage we're in for retrying a read from an advertisement
// GATT server.
AdvertisementReadResult::RetryStatus
AdvertisementReadResult::EvaluateRetryStatus() const {
MutexLock lock(&mutex_);
// Check if we have already succeeded reading this advertisement.
if (status_ == Status::kSuccess) {
return RetryStatus::kPreviouslySucceeded;
}
// Check if we have recently failed to read this advertisement.
if (GetDurationSinceReadLocked() < backoff_duration_) {
return RetryStatus::kTooSoon;
}
return RetryStatus::kRetry;
}
// Records the status of the latest read, and updates the next backoff
// duration for subsequent reads. Be sure to also call
// AddAdvertisement() if any advertisements were read.
void AdvertisementReadResult::RecordLastReadStatus(bool is_success) {
MutexLock lock(&mutex_);
// Update the last read timestamp.
last_read_timestamp_ = SystemClock::ElapsedRealtime();
// Update the backoff duration.
if (is_success) {
// Reset the backoff duration now that we had a successful read.
backoff_duration_ = config_.base_backoff_duration;
} else {
// Determine whether or not we were already failing before. If we were, we
// should increase the backoff duration.
if (status_ == Status::kFailure) {
// Use exponential backoff to determine the next backoff duration. This
// simply involves multiplying our current backoff duration by some
// multiplier.
absl::Duration next_backoff_duration =
config_.backoff_multiplier * backoff_duration_;
// Update the backoff duration, making sure not to blow past the
// ceiling.
backoff_duration_ =
std::min(next_backoff_duration, config_.max_backoff_duration);
} else {
// This is our first time failing, so we should only backoff for the
// initial duration.
backoff_duration_ = config_.base_backoff_duration;
}
}
// Update the internal result.
status_ = is_success ? Status::kSuccess : Status::kFailure;
}
// Returns how much time has passed since we last tried reading from an
// advertisement GATT server.
absl::Duration AdvertisementReadResult::GetDurationSinceRead() const {
MutexLock lock(&mutex_);
return GetDurationSinceReadLocked();
}
absl::Duration AdvertisementReadResult::GetDurationSinceReadLocked() const {
return SystemClock::ElapsedRealtime() - last_read_timestamp_;
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,104 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_MEDIUMS_ADVERTISEMENT_READ_RESULT_H_
#define CORE_V2_INTERNAL_MEDIUMS_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 "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/time/clock.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.
class AdvertisementReadResult {
public:
// 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).
struct Config {
// How much to multiply the backoff duration by with every failure to read
// from the advertisement GATT server. This should never be below 1!
float backoff_multiplier;
// The initial backoff duration when we fail to read from an advertisement
// GATT server.
absl::Duration base_backoff_duration;
// The maximum backoff duration allowed between advertisement GATT server
// reads.
absl::Duration max_backoff_duration;
};
static const Config kDefaultConfig;
explicit AdvertisementReadResult(const Config& config = kDefaultConfig)
: config_(config) {}
~AdvertisementReadResult() = default;
enum class RetryStatus {
kUnknown = 0,
kRetry = 1,
kPreviouslySucceeded = 2,
kTooSoon = 3,
};
void AddAdvertisement(std::int32_t slot, const ByteArray& advertisement)
ABSL_LOCKS_EXCLUDED(mutex_);
bool HasAdvertisement(std::int32_t slot) const ABSL_LOCKS_EXCLUDED(mutex_);
std::vector<const ByteArray*> GetAdvertisements() const
ABSL_LOCKS_EXCLUDED(mutex_);
RetryStatus EvaluateRetryStatus() const ABSL_LOCKS_EXCLUDED(mutex_);
void RecordLastReadStatus(bool is_success) ABSL_LOCKS_EXCLUDED(mutex_);
absl::Duration GetDurationSinceRead() const ABSL_LOCKS_EXCLUDED(mutex_);
private:
enum class Status {
kUnknown = 0,
kSuccess = 1,
kFailure = 2,
};
absl::Duration GetDurationSinceReadLocked() const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable Mutex mutex_;
// Maps slot numbers to the GATT advertisement found in that slot.
absl::flat_hash_map<std::int32_t, ByteArray> advertisements_
ABSL_GUARDED_BY(mutex_);
Config config_;
absl::Duration backoff_duration_ ABSL_GUARDED_BY(mutex_);
absl::Time last_read_timestamp_ ABSL_GUARDED_BY(mutex_);
Status status_ ABSL_GUARDED_BY(mutex_) = Status::kUnknown;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_MEDIUMS_ADVERTISEMENT_READ_RESULT_H_
@@ -0,0 +1,143 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/mediums/advertisement_read_result.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
constexpr char kAdvertisementBytes[] = "\x0A\x0B\x0C";
// 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::Seconds(1);
const absl::Duration kAdvertisementMaxBackoffDuration = absl::Seconds(6);
const AdvertisementReadResult::Config test_config{
.backoff_multiplier =
AdvertisementReadResult::kDefaultConfig.backoff_multiplier,
.base_backoff_duration = kAdvertisementBaseBackoffDuration,
.max_backoff_duration = kAdvertisementMaxBackoffDuration,
};
TEST(AdvertisementReadResultTest, AdvertisementExists) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
advertisement_read_result.AddAdvertisement(slot,
ByteArray(kAdvertisementBytes));
EXPECT_TRUE(advertisement_read_result.HasAdvertisement(slot));
}
TEST(AdvertisementReadResultTest, AdvertisementNonExistent) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
EXPECT_FALSE(advertisement_read_result.HasAdvertisement(slot));
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusInitialized) {
AdvertisementReadResult advertisement_read_result(test_config);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kRetry);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusSuccess) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kPreviouslySucceeded);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusTooSoon) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ false);
// Sleep for some time, but not long enough to warrant a retry.
absl::SleepFor(kAdvertisementBaseBackoffDuration / 2);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kTooSoon);
}
TEST(AdvertisementReadResultTest, EvaluateRetryStatusRetry) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ false);
// Sleep long enough to warrant a retry.
absl::SleepFor(kAdvertisementBaseBackoffDuration);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kRetry);
}
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoff) {
AdvertisementReadResult advertisement_read_result(test_config);
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);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kTooSoon);
}
TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoffMax) {
AdvertisementReadResult advertisement_read_result(test_config);
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);
EXPECT_EQ(advertisement_read_result.EvaluateRetryStatus(),
AdvertisementReadResult::RetryStatus::kRetry);
}
TEST(AdvertisementReadResultTest, GetDurationSinceRead) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
absl::Duration sleepTime = absl::Milliseconds(420);
absl::SleepFor(sleepTime);
EXPECT_GE(advertisement_read_result.GetDurationSinceRead(), sleepTime);
}
} // namespace
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,215 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/mediums/ble_advertisement.h"
#include <inttypes.h>
#include "platform_v2/public/logging.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
BleAdvertisement::BleAdvertisement(Version version,
SocketVersion socket_version,
const ByteArray &service_id_hash,
const ByteArray &data) {
// Check that the given input is valid.
if (!IsSupportedVersion(version) ||
!IsSupportedSocketVersion(socket_version) ||
service_id_hash.size() != kServiceIdHashLength ||
data.size() > kMaxDataSize) {
return;
}
version_ = version;
socket_version_ = socket_version;
service_id_hash_ = service_id_hash;
data_ = data;
}
BleAdvertisement::BleAdvertisement(const ByteArray &ble_advertisement_bytes) {
if (ble_advertisement_bytes.Empty()) {
NEARBY_LOG(INFO,
"Cannot deserialize BleAdvertisement: null bytes passed in.");
return;
}
if (ble_advertisement_bytes.size() < kMinAdvertisementLength) {
NEARBY_LOG(INFO,
"Cannot deserialize BleAdvertisement: expecting min %d raw "
"bytes, got %" PRIu64,
kMinAdvertisementLength, ble_advertisement_bytes.size());
return;
}
// Now, time to read the bytes!
const auto *read_ptr = ble_advertisement_bytes.data();
// 1. Version.
version_ = static_cast<Version>((*read_ptr & kVersionBitmask) >> 5);
if (!IsSupportedVersion(version_)) {
NEARBY_LOG(INFO,
"Cannot deserialize BleAdvertisement: unsupported Version %u",
version_);
return;
}
// 2. Socket Version.
socket_version_ =
static_cast<SocketVersion>((*read_ptr & kSocketVersionBitmask) >> 2);
if (!IsSupportedSocketVersion(socket_version_)) {
NEARBY_LOG(
INFO,
"Cannot deserialize BLEAdvertisement: unsupported SocketVersion %u",
socket_version_);
version_ = Version::kUndefined;
return;
}
read_ptr += kVersionLength;
// 3. Service ID hash.
service_id_hash_ = ByteArray(read_ptr, kServiceIdHashLength);
read_ptr += kServiceIdHashLength;
// 4.1. Data size.
size_t expected_data_size = DeserializeDataSize(read_ptr);
if (expected_data_size < 0) {
NEARBY_LOG(
INFO,
"Cannot deserialize BleAdvertisement: negative data size %" PRIu64,
expected_data_size);
version_ = Version::kUndefined;
return;
}
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 %" PRIu64 " bytes",
expected_data_size, actual_data_size);
version_ = Version::kUndefined;
return;
}
// 4.2. Data.
data_ = ByteArray(read_ptr, expected_data_size);
read_ptr += expected_data_size;
}
BleAdvertisement::operator ByteArray() const {
if (!IsValid()) {
return ByteArray{};
}
std::string out;
// The first 3 bits are the Version.
char version_and_socket_version_byte =
(static_cast<char>(version_) << 5) & kVersionBitmask;
// The next 3 bits are the Socket version. 2 bits left are reserved.
version_and_socket_version_byte |=
(static_cast<char>(socket_version_) << 2) & kSocketVersionBitmask;
// Serialize Data size bytes(4).
ByteArray data_size_bytes{kDataSizeLength};
auto *data_size_bytes_write_ptr = data_size_bytes.data();
SerializeDataSize(data_size_bytes_write_ptr, data_.size());
out.reserve(1 + service_id_hash_.size() + 1 + data_.size());
out.append(1, version_and_socket_version_byte);
out.append(std::string(service_id_hash_));
out.append(std::string(data_size_bytes));
out.append(std::string(data_));
return ByteArray{std::move(out)};
}
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();
}
bool BleAdvertisement::IsSupportedVersion(Version version) const {
return version >= Version::kV1 && version <= Version::kV2;
}
bool BleAdvertisement::IsSupportedSocketVersion(
SocketVersion socket_version) const {
return socket_version >= SocketVersion::kV1 &&
socket_version <= SocketVersion::kV2;
}
void BleAdvertisement::SerializeDataSize(char *data_size_bytes_write_ptr,
size_t data_size) const {
// 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];
}
}
size_t BleAdvertisement::DeserializeDataSize(
const char *data_size_bytes_read_ptr) const {
// 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(
const ByteArray &ble_advertisement_bytes) const {
return ble_advertisement_bytes.size() - kMinAdvertisementLength;
}
size_t BleAdvertisement::ComputeAdvertisementLength(
const ByteArray &data) const {
// The advertisement length is the minimum length + the length of the data.
return kMinAdvertisementLength + data.size();
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,114 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
#define CORE_V2_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
#include <utility>
#include "platform_v2/base/byte_array.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.
enum class Version {
kUndefined = 0,
kV1 = 1,
kV2 = 2,
// Version is only allocated 3 bits in the BleAdvertisement, so this can
// never go beyond V7.
};
// Versions of the BLESocket.
enum class SocketVersion {
kUndefined = 0,
kV1 = 1,
kV2 = 2,
// SocketVersion is only allocated 3 bits in the BleAdvertisement, so this
// can never go beyond V7.
};
static constexpr int kServiceIdHashLength = 3;
BleAdvertisement() = default;
BleAdvertisement(Version version, SocketVersion socket_version,
const ByteArray &service_id_hash, const ByteArray &data);
explicit BleAdvertisement(const ByteArray &ble_advertisement_bytes);
BleAdvertisement(const BleAdvertisement &) = default;
BleAdvertisement &operator=(const BleAdvertisement &) = default;
BleAdvertisement(BleAdvertisement &&) = default;
BleAdvertisement &operator=(BleAdvertisement &&) = default;
~BleAdvertisement() = default;
explicit operator ByteArray() const;
// Operator overloads when comparing BleAdvertisement.
bool operator==(const BleAdvertisement &rhs) const;
bool operator<(const BleAdvertisement &rhs) const;
bool IsValid() const { return IsSupportedVersion(version_); }
Version GetVersion() const { return version_; }
SocketVersion GetSocketVersion() const { return socket_version_; }
ByteArray GetServiceIdHash() const { return service_id_hash_; }
ByteArray &GetData() & { return data_; }
const ByteArray &GetData() const & { return data_; }
ByteArray &&GetData() && { return std::move(data_); }
const ByteArray &&GetData() const && { return std::move(data_); }
private:
bool IsSupportedVersion(Version version) const;
bool IsSupportedSocketVersion(SocketVersion socket_version) const;
void SerializeDataSize(char *data_size_bytes_write_ptr,
size_t data_size) const;
size_t DeserializeDataSize(const char *data_size_bytes_read_ptr) const;
size_t ComputeDataSize(const ByteArray &ble_advertisement_bytes) const;
size_t ComputeAdvertisementLength(const ByteArray &data) const;
static constexpr int kVersionLength = 1;
// Length of one int. Be sure to re-evaluate how we compute data size in this
// class if this constant ever changes!
static constexpr int kDataSizeLength = 4;
static constexpr int 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.
static constexpr int kMaxGattCharacteristicValueSize = 512;
static constexpr int kMaxDataSize =
kMaxGattCharacteristicValueSize - kMinAdvertisementLength;
static constexpr int kVersionBitmask = 0x0E0;
static constexpr int kSocketVersionBitmask = 0x01C;
Version version_{Version::kUndefined};
SocketVersion socket_version_{SocketVersion::kUndefined};
ByteArray service_id_hash_;
ByteArray data_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_ADVERTISEMENT_H_
@@ -0,0 +1,132 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "core_v2/internal/mediums/ble_advertisement_header.h"
#include <inttypes.h>
#include "platform_v2/base/base64_utils.h"
#include "platform_v2/public/logging.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
BleAdvertisementHeader::BleAdvertisementHeader(
Version version, int num_slots, const ByteArray &service_id_bloom_filter,
const ByteArray &advertisement_hash) {
// TODO(edwinwu): Checks if num_slots needs to be >= 0
if (version != Version::kV2 ||
service_id_bloom_filter.size() != kServiceIdBloomFilterLength ||
advertisement_hash.size() != kAdvertisementHashLength) {
return;
}
version_ = version;
num_slots_ = num_slots;
service_id_bloom_filter_ = service_id_bloom_filter;
advertisement_hash_ = advertisement_hash;
}
BleAdvertisementHeader::BleAdvertisementHeader(
const std::string &ble_advertisement_header_string) {
ByteArray ble_advertisement_header_bytes =
Base64Utils::Decode(ble_advertisement_header_string);
if (ble_advertisement_header_bytes.Empty()) {
NEARBY_LOG(
ERROR,
"Cannot deserialize BLEAdvertisementHeader: failed Base64 decoding");
return;
}
if (ble_advertisement_header_bytes.size() < kMinAdvertisementHeaderLength) {
NEARBY_LOG(ERROR,
"Cannot deserialize BleAdvertisementHeader: expecting min %u "
"raw bytes, got %" PRIu64 " instead",
kMinAdvertisementHeaderLength,
ble_advertisement_header_bytes.size());
return;
}
// Start reading the bytes.
auto *ble_advertisement_header_read_ptr =
ble_advertisement_header_bytes.data();
// The first 3 bits are supposed to be the version.
version_ = static_cast<Version>(
(*ble_advertisement_header_read_ptr & kVersionBitmask) >> 5);
if (version_ != Version::kV2) {
NEARBY_LOG(
ERROR,
"Cannot deserialize BleAdvertisementHeader: unsupported Version %d",
version_);
return;
}
// The last 5 bits of the first byte represent the number of slots.
num_slots_ = static_cast<std::uint32_t>(*ble_advertisement_header_read_ptr &
kNumSlotsBitmask);
ble_advertisement_header_read_ptr++;
// Service ID bloom filter.
service_id_bloom_filter_ =
ByteArray(ble_advertisement_header_read_ptr, kServiceIdBloomFilterLength);
ble_advertisement_header_read_ptr += kServiceIdBloomFilterLength;
// Advertisement hash.
advertisement_hash_ =
ByteArray(ble_advertisement_header_read_ptr, kAdvertisementHashLength);
ble_advertisement_header_read_ptr += kAdvertisementHashLength;
}
BleAdvertisementHeader::operator std::string() const {
if (!IsValid()) {
return "";
}
std::string out;
// The first 3 bits are the Version.
char version_and_num_slots_byte =
(static_cast<char>(version_) << 5) & kVersionBitmask;
// The next 5 bits are the number of slots.
version_and_num_slots_byte |=
static_cast<char>(num_slots_) & kNumSlotsBitmask;
out.reserve(1 + service_id_bloom_filter_.size() + advertisement_hash_.size());
out.append(1, version_and_num_slots_byte);
out.append(std::string(service_id_bloom_filter_));
out.append(std::string(advertisement_hash_));
return Base64Utils::Encode(ByteArray(std::move(out)));
}
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

Some files were not shown because too many files have changed in this diff Show More