Merge remote-tracking branch 'nearby/main'

# Conflicts:
#	connections/implementation/bwu_manager_test.cc
#	sharing/BUILD
#	sharing/certificates/fake_nearby_share_certificate_manager.cc
#	sharing/certificates/fake_nearby_share_certificate_manager.h
#	sharing/internal/base/utf_string_conversions.h
This commit is contained in:
Lasan Mahaliyana
2026-07-26 22:46:37 +05:30
155 changed files with 5257 additions and 2018 deletions
+3
View File
@@ -535,6 +535,7 @@ let package = Package(
.headerSearchPath("third_party/ukey2/ukey2/"),
.headerSearchPath("third_party/ukey2/compiled_proto/src/main/proto"),
.define("NO_WEBRTC"),
.define("GITHUB_BUILD"),
]
),
.target(
@@ -553,6 +554,7 @@ let package = Package(
.headerSearchPath("compiled_proto/"),
.define("NO_WEBRTC"),
.define("NC_OSS_BUILD"),
.define("GITHUB_BUILD"),
]
),
.target(
@@ -566,6 +568,7 @@ let package = Package(
.headerSearchPath("./"),
.headerSearchPath("compiled_proto/"),
.define("NO_WEBRTC"),
.define("GITHUB_BUILD"),
]
),
.target(
@@ -977,6 +977,9 @@ inline constexpr ConnectionResponseFrame::Impl_::Impl_(
handshake_data_(
&::google::protobuf::internal::fixed_address_empty_string,
::_pbi::ConstantInitialized()),
wifi_direct_device_name_(
&::google::protobuf::internal::fixed_address_empty_string,
::_pbi::ConstantInitialized()),
os_info_{nullptr},
location_hint_{nullptr},
status_{0},
@@ -4110,7 +4113,8 @@ PROTOBUF_NDEBUG_INLINE ConnectionResponseFrame::Impl_::Impl_(
[[maybe_unused]] const ::location::nearby::connections::ConnectionResponseFrame& from_msg)
: _has_bits_{from._has_bits_},
_cached_size_{0},
handshake_data_(arena, from.handshake_data_) {}
handshake_data_(arena, from.handshake_data_),
wifi_direct_device_name_(arena, from.wifi_direct_device_name_) {}
ConnectionResponseFrame::ConnectionResponseFrame(
::google::protobuf::Arena* PROTOBUF_NULLABLE arena,
@@ -4126,10 +4130,10 @@ ConnectionResponseFrame::ConnectionResponseFrame(
from._internal_metadata_);
new (&_impl_) Impl_(internal_visibility(), arena, from._impl_, from);
::uint32_t cached_has_bits = _impl_._has_bits_[0];
_impl_.os_info_ = (CheckHasBit(cached_has_bits, 0x00000002U))
_impl_.os_info_ = (CheckHasBit(cached_has_bits, 0x00000004U))
? ::google::protobuf::MessageLite::CopyConstruct(arena, *from._impl_.os_info_)
: nullptr;
_impl_.location_hint_ = (CheckHasBit(cached_has_bits, 0x00000004U))
_impl_.location_hint_ = (CheckHasBit(cached_has_bits, 0x00000008U))
? ::google::protobuf::MessageLite::CopyConstruct(arena, *from._impl_.location_hint_)
: nullptr;
::memcpy(reinterpret_cast<char*>(&_impl_) +
@@ -4146,7 +4150,8 @@ PROTOBUF_NDEBUG_INLINE ConnectionResponseFrame::Impl_::Impl_(
[[maybe_unused]] ::google::protobuf::internal::InternalVisibility visibility,
[[maybe_unused]] ::google::protobuf::Arena* PROTOBUF_NULLABLE arena)
: _cached_size_{0},
handshake_data_(arena) {}
handshake_data_(arena),
wifi_direct_device_name_(arena) {}
inline void ConnectionResponseFrame::SharedCtor(::_pb::Arena* PROTOBUF_NULLABLE arena) {
new (&_impl_) Impl_(internal_visibility(), arena);
@@ -4169,6 +4174,7 @@ inline void ConnectionResponseFrame::SharedDtor(MessageLite& self) {
this_._internal_metadata_.Delete<::std::string>();
ABSL_DCHECK(this_.GetArena() == nullptr);
this_._impl_.handshake_data_.Destroy();
this_._impl_.wifi_direct_device_name_.Destroy();
delete this_._impl_.os_info_;
delete this_._impl_.location_hint_;
this_._impl_.~Impl_();
@@ -4214,16 +4220,16 @@ ConnectionResponseFrame::GetClassData() const {
return ConnectionResponseFrame_class_data_.base();
}
PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
const ::_pbi::TcParseTable<4, 9, 3, 0, 2>
const ::_pbi::TcParseTable<4, 10, 3, 0, 2>
ConnectionResponseFrame::_table_ = {
{
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_._has_bits_),
0, // no _extensions_
9, 120, // max_field_number, fast_idx_mask
10, 120, // max_field_number, fast_idx_mask
offsetof(decltype(_table_), field_lookup_table),
4294966784, // skipmap
4294966272, // skipmap
offsetof(decltype(_table_), field_entries),
9, // num_field_entries
10, // num_field_entries
3, // num_aux_entries
offsetof(decltype(_table_), aux_entries),
ConnectionResponseFrame_class_data_.base(),
@@ -4236,7 +4242,7 @@ ConnectionResponseFrame::_table_ = {
{::_pbi::TcParser::MiniParse, {}},
// optional int32 status = 1 [deprecated = true];
{::_pbi::TcParser::FastV32S1,
{8, 3, 0,
{8, 4, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.status_)}},
// optional bytes handshake_data = 2;
{::_pbi::TcParser::FastBS1,
@@ -4244,33 +4250,36 @@ ConnectionResponseFrame::_table_ = {
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.handshake_data_)}},
// optional .location.nearby.connections.ConnectionResponseFrame.ResponseStatus response = 3;
{::_pbi::TcParser::FastEr0S1,
{24, 4, 2,
{24, 5, 2,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.response_)}},
// optional .location.nearby.connections.OsInfo os_info = 4;
{::_pbi::TcParser::FastMtS1,
{34, 1, 0,
{34, 2, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.os_info_)}},
// optional int32 multiplex_socket_bitmask = 5;
{::_pbi::TcParser::FastV32S1,
{40, 5, 0,
{40, 6, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.multiplex_socket_bitmask_)}},
// optional int32 nearby_connections_version = 6 [deprecated = true];
{::_pbi::TcParser::FastV32S1,
{48, 6, 0,
{48, 7, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.nearby_connections_version_)}},
// optional int32 safe_to_disconnect_version = 7;
{::_pbi::TcParser::FastV32S1,
{56, 7, 0,
{56, 8, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.safe_to_disconnect_version_)}},
// optional .location.nearby.connections.LocationHint location_hint = 8;
{::_pbi::TcParser::FastMtS1,
{66, 2, 1,
{66, 3, 1,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.location_hint_)}},
// optional int32 keep_alive_timeout_millis = 9;
{::_pbi::TcParser::FastV32S1,
{72, 8, 0,
{72, 9, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.keep_alive_timeout_millis_)}},
{::_pbi::TcParser::MiniParse, {}},
// optional string wifi_direct_device_name = 10;
{::_pbi::TcParser::FastBS1,
{82, 1, 0,
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.wifi_direct_device_name_)}},
{::_pbi::TcParser::MiniParse, {}},
{::_pbi::TcParser::MiniParse, {}},
{::_pbi::TcParser::MiniParse, {}},
@@ -4280,23 +4289,25 @@ ConnectionResponseFrame::_table_ = {
65535, 65535
}}, {{
// optional int32 status = 1 [deprecated = true];
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.status_), _Internal::kHasBitsOffset + 3, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.status_), _Internal::kHasBitsOffset + 4, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
// optional bytes handshake_data = 2;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.handshake_data_), _Internal::kHasBitsOffset + 0, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)},
// optional .location.nearby.connections.ConnectionResponseFrame.ResponseStatus response = 3;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.response_), _Internal::kHasBitsOffset + 4, 2, (0 | ::_fl::kFcOptional | ::_fl::kEnumRange)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.response_), _Internal::kHasBitsOffset + 5, 2, (0 | ::_fl::kFcOptional | ::_fl::kEnumRange)},
// optional .location.nearby.connections.OsInfo os_info = 4;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.os_info_), _Internal::kHasBitsOffset + 1, 0, (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.os_info_), _Internal::kHasBitsOffset + 2, 0, (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
// optional int32 multiplex_socket_bitmask = 5;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.multiplex_socket_bitmask_), _Internal::kHasBitsOffset + 5, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.multiplex_socket_bitmask_), _Internal::kHasBitsOffset + 6, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
// optional int32 nearby_connections_version = 6 [deprecated = true];
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.nearby_connections_version_), _Internal::kHasBitsOffset + 6, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.nearby_connections_version_), _Internal::kHasBitsOffset + 7, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
// optional int32 safe_to_disconnect_version = 7;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.safe_to_disconnect_version_), _Internal::kHasBitsOffset + 7, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.safe_to_disconnect_version_), _Internal::kHasBitsOffset + 8, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
// optional .location.nearby.connections.LocationHint location_hint = 8;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.location_hint_), _Internal::kHasBitsOffset + 2, 1, (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.location_hint_), _Internal::kHasBitsOffset + 3, 1, (0 | ::_fl::kFcOptional | ::_fl::kMessage | ::_fl::kTvTable)},
// optional int32 keep_alive_timeout_millis = 9;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.keep_alive_timeout_millis_), _Internal::kHasBitsOffset + 8, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.keep_alive_timeout_millis_), _Internal::kHasBitsOffset + 9, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},
// optional string wifi_direct_device_name = 10;
{PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.wifi_direct_device_name_), _Internal::kHasBitsOffset + 1, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)},
}},
{{
{::_pbi::TcParser::GetTable<::location::nearby::connections::OsInfo>()},
@@ -4314,25 +4325,32 @@ PROTOBUF_NOINLINE void ConnectionResponseFrame::Clear() {
(void) cached_has_bits;
cached_has_bits = _impl_._has_bits_[0];
if (BatchCheckHasBit(cached_has_bits, 0x00000007U)) {
if (BatchCheckHasBit(cached_has_bits, 0x0000000fU)) {
if (CheckHasBit(cached_has_bits, 0x00000001U)) {
_impl_.handshake_data_.ClearNonDefaultToEmpty();
}
if (CheckHasBit(cached_has_bits, 0x00000002U)) {
_impl_.wifi_direct_device_name_.ClearNonDefaultToEmpty();
}
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
ABSL_DCHECK(_impl_.os_info_ != nullptr);
_impl_.os_info_->Clear();
}
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
ABSL_DCHECK(_impl_.location_hint_ != nullptr);
_impl_.location_hint_->Clear();
}
}
if (BatchCheckHasBit(cached_has_bits, 0x000000f8U)) {
if (BatchCheckHasBit(cached_has_bits, 0x000000f0U)) {
::memset(&_impl_.status_, 0, static_cast<::size_t>(
reinterpret_cast<char*>(&_impl_.safe_to_disconnect_version_) -
reinterpret_cast<char*>(&_impl_.status_)) + sizeof(_impl_.safe_to_disconnect_version_));
reinterpret_cast<char*>(&_impl_.nearby_connections_version_) -
reinterpret_cast<char*>(&_impl_.status_)) + sizeof(_impl_.nearby_connections_version_));
}
if (BatchCheckHasBit(cached_has_bits, 0x00000300U)) {
::memset(&_impl_.safe_to_disconnect_version_, 0, static_cast<::size_t>(
reinterpret_cast<char*>(&_impl_.keep_alive_timeout_millis_) -
reinterpret_cast<char*>(&_impl_.safe_to_disconnect_version_)) + sizeof(_impl_.keep_alive_timeout_millis_));
}
_impl_.keep_alive_timeout_millis_ = 0;
_impl_._has_bits_.Clear();
_internal_metadata_.Clear<::std::string>();
}
@@ -4357,7 +4375,7 @@ PROTOBUF_NOINLINE void ConnectionResponseFrame::Clear() {
cached_has_bits = this_._impl_._has_bits_[0];
// optional int32 status = 1 [deprecated = true];
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
target =
::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<1>(
stream, this_._internal_status(), target);
@@ -4370,54 +4388,60 @@ PROTOBUF_NOINLINE void ConnectionResponseFrame::Clear() {
}
// optional .location.nearby.connections.ConnectionResponseFrame.ResponseStatus response = 3;
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteEnumToArray(
3, this_._internal_response(), target);
}
// optional .location.nearby.connections.OsInfo os_info = 4;
if (CheckHasBit(cached_has_bits, 0x00000002U)) {
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
target = ::google::protobuf::internal::WireFormatLite::InternalWriteMessage(
4, *this_._impl_.os_info_, this_._impl_.os_info_->GetCachedSize(), target,
stream);
}
// optional int32 multiplex_socket_bitmask = 5;
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
target =
::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<5>(
stream, this_._internal_multiplex_socket_bitmask(), target);
}
// optional int32 nearby_connections_version = 6 [deprecated = true];
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
target =
::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<6>(
stream, this_._internal_nearby_connections_version(), target);
}
// optional int32 safe_to_disconnect_version = 7;
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
target =
::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<7>(
stream, this_._internal_safe_to_disconnect_version(), target);
}
// optional .location.nearby.connections.LocationHint location_hint = 8;
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
target = ::google::protobuf::internal::WireFormatLite::InternalWriteMessage(
8, *this_._impl_.location_hint_, this_._impl_.location_hint_->GetCachedSize(), target,
stream);
}
// optional int32 keep_alive_timeout_millis = 9;
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
if (CheckHasBit(cached_has_bits, 0x00000200U)) {
target =
::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<9>(
stream, this_._internal_keep_alive_timeout_millis(), target);
}
// optional string wifi_direct_device_name = 10;
if (CheckHasBit(cached_has_bits, 0x00000002U)) {
const ::std::string& _s = this_._internal_wifi_direct_device_name();
target = stream->WriteStringMaybeAliased(10, _s, target);
}
if (ABSL_PREDICT_FALSE(this_._internal_metadata_.have_unknown_fields())) {
target = stream->WriteRaw(
this_._internal_metadata_.unknown_fields<::std::string>(::google::protobuf::internal::GetEmptyString).data(),
@@ -4449,45 +4473,50 @@ PROTOBUF_NOINLINE void ConnectionResponseFrame::Clear() {
total_size += 1 + ::google::protobuf::internal::WireFormatLite::BytesSize(
this_._internal_handshake_data());
}
// optional .location.nearby.connections.OsInfo os_info = 4;
// optional string wifi_direct_device_name = 10;
if (CheckHasBit(cached_has_bits, 0x00000002U)) {
total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize(
this_._internal_wifi_direct_device_name());
}
// optional .location.nearby.connections.OsInfo os_info = 4;
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::MessageSize(*this_._impl_.os_info_);
}
// optional .location.nearby.connections.LocationHint location_hint = 8;
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::MessageSize(*this_._impl_.location_hint_);
}
// optional int32 status = 1 [deprecated = true];
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
this_._internal_status());
}
// optional .location.nearby.connections.ConnectionResponseFrame.ResponseStatus response = 3;
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
total_size += 1 +
::_pbi::WireFormatLite::EnumSize(this_._internal_response());
}
// optional int32 multiplex_socket_bitmask = 5;
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
this_._internal_multiplex_socket_bitmask());
}
// optional int32 nearby_connections_version = 6 [deprecated = true];
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
this_._internal_nearby_connections_version());
}
}
if (BatchCheckHasBit(cached_has_bits, 0x00000300U)) {
// optional int32 safe_to_disconnect_version = 7;
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
this_._internal_safe_to_disconnect_version());
}
}
{
// optional int32 keep_alive_timeout_millis = 9;
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
if (CheckHasBit(cached_has_bits, 0x00000200U)) {
total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(
this_._internal_keep_alive_timeout_millis());
}
@@ -4519,6 +4548,9 @@ void ConnectionResponseFrame::MergeImpl(::google::protobuf::MessageLite& to_msg,
_this->_internal_set_handshake_data(from._internal_handshake_data());
}
if (CheckHasBit(cached_has_bits, 0x00000002U)) {
_this->_internal_set_wifi_direct_device_name(from._internal_wifi_direct_device_name());
}
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
ABSL_DCHECK(from._impl_.os_info_ != nullptr);
if (_this->_impl_.os_info_ == nullptr) {
_this->_impl_.os_info_ = ::google::protobuf::MessageLite::CopyConstruct(arena, *from._impl_.os_info_);
@@ -4526,7 +4558,7 @@ void ConnectionResponseFrame::MergeImpl(::google::protobuf::MessageLite& to_msg,
_this->_impl_.os_info_->MergeFrom(*from._impl_.os_info_);
}
}
if (CheckHasBit(cached_has_bits, 0x00000004U)) {
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
ABSL_DCHECK(from._impl_.location_hint_ != nullptr);
if (_this->_impl_.location_hint_ == nullptr) {
_this->_impl_.location_hint_ = ::google::protobuf::MessageLite::CopyConstruct(arena, *from._impl_.location_hint_);
@@ -4534,24 +4566,26 @@ void ConnectionResponseFrame::MergeImpl(::google::protobuf::MessageLite& to_msg,
_this->_impl_.location_hint_->MergeFrom(*from._impl_.location_hint_);
}
}
if (CheckHasBit(cached_has_bits, 0x00000008U)) {
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
_this->_impl_.status_ = from._impl_.status_;
}
if (CheckHasBit(cached_has_bits, 0x00000010U)) {
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
_this->_impl_.response_ = from._impl_.response_;
}
if (CheckHasBit(cached_has_bits, 0x00000020U)) {
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
_this->_impl_.multiplex_socket_bitmask_ = from._impl_.multiplex_socket_bitmask_;
}
if (CheckHasBit(cached_has_bits, 0x00000040U)) {
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
_this->_impl_.nearby_connections_version_ = from._impl_.nearby_connections_version_;
}
if (CheckHasBit(cached_has_bits, 0x00000080U)) {
}
if (BatchCheckHasBit(cached_has_bits, 0x00000300U)) {
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
_this->_impl_.safe_to_disconnect_version_ = from._impl_.safe_to_disconnect_version_;
}
}
if (CheckHasBit(cached_has_bits, 0x00000100U)) {
_this->_impl_.keep_alive_timeout_millis_ = from._impl_.keep_alive_timeout_millis_;
if (CheckHasBit(cached_has_bits, 0x00000200U)) {
_this->_impl_.keep_alive_timeout_millis_ = from._impl_.keep_alive_timeout_millis_;
}
}
_this->_impl_._has_bits_[0] |= cached_has_bits;
_this->_internal_metadata_.MergeFrom<::std::string>(
@@ -4573,6 +4607,7 @@ void ConnectionResponseFrame::InternalSwap(ConnectionResponseFrame* PROTOBUF_RES
_internal_metadata_.InternalSwap(&other->_internal_metadata_);
swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
::_pbi::ArenaStringPtr::InternalSwap(&_impl_.handshake_data_, &other->_impl_.handshake_data_, arena);
::_pbi::ArenaStringPtr::InternalSwap(&_impl_.wifi_direct_device_name_, &other->_impl_.wifi_direct_device_name_, arena);
::google::protobuf::internal::memswap<
PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, _impl_.keep_alive_timeout_millis_)
+ sizeof(ConnectionResponseFrame::_impl_.keep_alive_timeout_millis_)
@@ -8114,6 +8114,7 @@ class ConnectionResponseFrame final : public ::google::protobuf::MessageLite
// accessors -------------------------------------------------------
enum : int {
kHandshakeDataFieldNumber = 2,
kWifiDirectDeviceNameFieldNumber = 10,
kOsInfoFieldNumber = 4,
kLocationHintFieldNumber = 8,
kStatusFieldNumber = 1,
@@ -8138,6 +8139,22 @@ class ConnectionResponseFrame final : public ::google::protobuf::MessageLite
PROTOBUF_ALWAYS_INLINE void _internal_set_handshake_data(const ::std::string& value);
::std::string* PROTOBUF_NONNULL _internal_mutable_handshake_data();
public:
// optional string wifi_direct_device_name = 10;
bool has_wifi_direct_device_name() const;
void clear_wifi_direct_device_name() ;
const ::std::string& wifi_direct_device_name() const;
template <typename Arg_ = const ::std::string&, typename... Args_>
void set_wifi_direct_device_name(Arg_&& arg, Args_... args);
::std::string* PROTOBUF_NONNULL mutable_wifi_direct_device_name();
[[nodiscard]] ::std::string* PROTOBUF_NULLABLE release_wifi_direct_device_name();
void set_allocated_wifi_direct_device_name(::std::string* PROTOBUF_NULLABLE value);
private:
const ::std::string& _internal_wifi_direct_device_name() const;
PROTOBUF_ALWAYS_INLINE void _internal_set_wifi_direct_device_name(const ::std::string& value);
::std::string* PROTOBUF_NONNULL _internal_mutable_wifi_direct_device_name();
public:
// optional .location.nearby.connections.OsInfo os_info = 4;
bool has_os_info() const;
@@ -8239,7 +8256,7 @@ class ConnectionResponseFrame final : public ::google::protobuf::MessageLite
private:
class _Internal;
friend class ::google::protobuf::internal::TcParser;
static const ::google::protobuf::internal::TcParseTable<4, 9,
static const ::google::protobuf::internal::TcParseTable<4, 10,
3, 0,
2>
_table_;
@@ -8262,6 +8279,7 @@ class ConnectionResponseFrame final : public ::google::protobuf::MessageLite
::google::protobuf::internal::HasBits<1> _has_bits_;
::google::protobuf::internal::CachedSize _cached_size_;
::google::protobuf::internal::ArenaStringPtr handshake_data_;
::google::protobuf::internal::ArenaStringPtr wifi_direct_device_name_;
::location::nearby::connections::OsInfo* PROTOBUF_NULLABLE os_info_;
::location::nearby::connections::LocationHint* PROTOBUF_NULLABLE location_hint_;
::int32_t status_;
@@ -13226,14 +13244,14 @@ inline ConnectionRequestFrame::DeviceCase ConnectionRequestFrame::Device_case()
// optional int32 status = 1 [deprecated = true];
inline bool ConnectionResponseFrame::has_status() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000008U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000010U);
return value;
}
inline void ConnectionResponseFrame::clear_status() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.status_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000008U);
0x00000010U);
}
inline ::int32_t ConnectionResponseFrame::status() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.status)
@@ -13241,7 +13259,7 @@ inline ::int32_t ConnectionResponseFrame::status() const {
}
inline void ConnectionResponseFrame::set_status(::int32_t value) {
_internal_set_status(value);
SetHasBit(_impl_._has_bits_[0], 0x00000008U);
SetHasBit(_impl_._has_bits_[0], 0x00000010U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.status)
}
inline ::int32_t ConnectionResponseFrame::_internal_status() const {
@@ -13324,14 +13342,14 @@ inline void ConnectionResponseFrame::set_allocated_handshake_data(::std::string*
// optional .location.nearby.connections.ConnectionResponseFrame.ResponseStatus response = 3;
inline bool ConnectionResponseFrame::has_response() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000010U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000020U);
return value;
}
inline void ConnectionResponseFrame::clear_response() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.response_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000010U);
0x00000020U);
}
inline ::location::nearby::connections::ConnectionResponseFrame_ResponseStatus ConnectionResponseFrame::response() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.response)
@@ -13339,7 +13357,7 @@ inline ::location::nearby::connections::ConnectionResponseFrame_ResponseStatus C
}
inline void ConnectionResponseFrame::set_response(::location::nearby::connections::ConnectionResponseFrame_ResponseStatus value) {
_internal_set_response(value);
SetHasBit(_impl_._has_bits_[0], 0x00000010U);
SetHasBit(_impl_._has_bits_[0], 0x00000020U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.response)
}
inline ::location::nearby::connections::ConnectionResponseFrame_ResponseStatus ConnectionResponseFrame::_internal_response() const {
@@ -13356,7 +13374,7 @@ inline void ConnectionResponseFrame::_internal_set_response(::location::nearby::
// optional .location.nearby.connections.OsInfo os_info = 4;
inline bool ConnectionResponseFrame::has_os_info() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000002U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000004U);
PROTOBUF_ASSUME(!value || _impl_.os_info_ != nullptr);
return value;
}
@@ -13364,7 +13382,7 @@ inline void ConnectionResponseFrame::clear_os_info() {
::google::protobuf::internal::TSanWrite(&_impl_);
if (_impl_.os_info_ != nullptr) _impl_.os_info_->Clear();
ClearHasBit(_impl_._has_bits_[0],
0x00000002U);
0x00000004U);
}
inline const ::location::nearby::connections::OsInfo& ConnectionResponseFrame::_internal_os_info() const {
::google::protobuf::internal::TSanRead(&_impl_);
@@ -13383,16 +13401,16 @@ inline void ConnectionResponseFrame::unsafe_arena_set_allocated_os_info(
}
_impl_.os_info_ = reinterpret_cast<::location::nearby::connections::OsInfo*>(value);
if (value != nullptr) {
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
} else {
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
}
// @@protoc_insertion_point(field_unsafe_arena_set_allocated:location.nearby.connections.ConnectionResponseFrame.os_info)
}
inline ::location::nearby::connections::OsInfo* PROTOBUF_NULLABLE ConnectionResponseFrame::release_os_info() {
::google::protobuf::internal::TSanWrite(&_impl_);
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
::location::nearby::connections::OsInfo* released = _impl_.os_info_;
_impl_.os_info_ = nullptr;
if (::google::protobuf::internal::DebugHardenForceCopyInRelease()) {
@@ -13412,7 +13430,7 @@ inline ::location::nearby::connections::OsInfo* PROTOBUF_NULLABLE ConnectionResp
::google::protobuf::internal::TSanWrite(&_impl_);
// @@protoc_insertion_point(field_release:location.nearby.connections.ConnectionResponseFrame.os_info)
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
::location::nearby::connections::OsInfo* temp = _impl_.os_info_;
_impl_.os_info_ = nullptr;
return temp;
@@ -13427,7 +13445,7 @@ inline ::location::nearby::connections::OsInfo* PROTOBUF_NONNULL ConnectionRespo
}
inline ::location::nearby::connections::OsInfo* PROTOBUF_NONNULL ConnectionResponseFrame::mutable_os_info()
ABSL_ATTRIBUTE_LIFETIME_BOUND {
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
::location::nearby::connections::OsInfo* _msg = _internal_mutable_os_info();
// @@protoc_insertion_point(field_mutable:location.nearby.connections.ConnectionResponseFrame.os_info)
return _msg;
@@ -13444,9 +13462,9 @@ inline void ConnectionResponseFrame::set_allocated_os_info(::location::nearby::c
if (message_arena != submessage_arena) {
value = ::google::protobuf::internal::GetOwnedMessage(message_arena, value, submessage_arena);
}
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
} else {
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
}
_impl_.os_info_ = reinterpret_cast<::location::nearby::connections::OsInfo*>(value);
@@ -13455,14 +13473,14 @@ inline void ConnectionResponseFrame::set_allocated_os_info(::location::nearby::c
// optional int32 multiplex_socket_bitmask = 5;
inline bool ConnectionResponseFrame::has_multiplex_socket_bitmask() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000020U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000040U);
return value;
}
inline void ConnectionResponseFrame::clear_multiplex_socket_bitmask() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.multiplex_socket_bitmask_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000020U);
0x00000040U);
}
inline ::int32_t ConnectionResponseFrame::multiplex_socket_bitmask() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.multiplex_socket_bitmask)
@@ -13470,7 +13488,7 @@ inline ::int32_t ConnectionResponseFrame::multiplex_socket_bitmask() const {
}
inline void ConnectionResponseFrame::set_multiplex_socket_bitmask(::int32_t value) {
_internal_set_multiplex_socket_bitmask(value);
SetHasBit(_impl_._has_bits_[0], 0x00000020U);
SetHasBit(_impl_._has_bits_[0], 0x00000040U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.multiplex_socket_bitmask)
}
inline ::int32_t ConnectionResponseFrame::_internal_multiplex_socket_bitmask() const {
@@ -13484,14 +13502,14 @@ inline void ConnectionResponseFrame::_internal_set_multiplex_socket_bitmask(::in
// optional int32 nearby_connections_version = 6 [deprecated = true];
inline bool ConnectionResponseFrame::has_nearby_connections_version() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000040U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000080U);
return value;
}
inline void ConnectionResponseFrame::clear_nearby_connections_version() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.nearby_connections_version_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000040U);
0x00000080U);
}
inline ::int32_t ConnectionResponseFrame::nearby_connections_version() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.nearby_connections_version)
@@ -13499,7 +13517,7 @@ inline ::int32_t ConnectionResponseFrame::nearby_connections_version() const {
}
inline void ConnectionResponseFrame::set_nearby_connections_version(::int32_t value) {
_internal_set_nearby_connections_version(value);
SetHasBit(_impl_._has_bits_[0], 0x00000040U);
SetHasBit(_impl_._has_bits_[0], 0x00000080U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.nearby_connections_version)
}
inline ::int32_t ConnectionResponseFrame::_internal_nearby_connections_version() const {
@@ -13513,14 +13531,14 @@ inline void ConnectionResponseFrame::_internal_set_nearby_connections_version(::
// optional int32 safe_to_disconnect_version = 7;
inline bool ConnectionResponseFrame::has_safe_to_disconnect_version() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000080U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000100U);
return value;
}
inline void ConnectionResponseFrame::clear_safe_to_disconnect_version() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.safe_to_disconnect_version_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000080U);
0x00000100U);
}
inline ::int32_t ConnectionResponseFrame::safe_to_disconnect_version() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.safe_to_disconnect_version)
@@ -13528,7 +13546,7 @@ inline ::int32_t ConnectionResponseFrame::safe_to_disconnect_version() const {
}
inline void ConnectionResponseFrame::set_safe_to_disconnect_version(::int32_t value) {
_internal_set_safe_to_disconnect_version(value);
SetHasBit(_impl_._has_bits_[0], 0x00000080U);
SetHasBit(_impl_._has_bits_[0], 0x00000100U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.safe_to_disconnect_version)
}
inline ::int32_t ConnectionResponseFrame::_internal_safe_to_disconnect_version() const {
@@ -13542,7 +13560,7 @@ inline void ConnectionResponseFrame::_internal_set_safe_to_disconnect_version(::
// optional .location.nearby.connections.LocationHint location_hint = 8;
inline bool ConnectionResponseFrame::has_location_hint() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000004U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000008U);
PROTOBUF_ASSUME(!value || _impl_.location_hint_ != nullptr);
return value;
}
@@ -13550,7 +13568,7 @@ inline void ConnectionResponseFrame::clear_location_hint() {
::google::protobuf::internal::TSanWrite(&_impl_);
if (_impl_.location_hint_ != nullptr) _impl_.location_hint_->Clear();
ClearHasBit(_impl_._has_bits_[0],
0x00000004U);
0x00000008U);
}
inline const ::location::nearby::connections::LocationHint& ConnectionResponseFrame::_internal_location_hint() const {
::google::protobuf::internal::TSanRead(&_impl_);
@@ -13569,16 +13587,16 @@ inline void ConnectionResponseFrame::unsafe_arena_set_allocated_location_hint(
}
_impl_.location_hint_ = reinterpret_cast<::location::nearby::connections::LocationHint*>(value);
if (value != nullptr) {
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
SetHasBit(_impl_._has_bits_[0], 0x00000008U);
} else {
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
ClearHasBit(_impl_._has_bits_[0], 0x00000008U);
}
// @@protoc_insertion_point(field_unsafe_arena_set_allocated:location.nearby.connections.ConnectionResponseFrame.location_hint)
}
inline ::location::nearby::connections::LocationHint* PROTOBUF_NULLABLE ConnectionResponseFrame::release_location_hint() {
::google::protobuf::internal::TSanWrite(&_impl_);
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
ClearHasBit(_impl_._has_bits_[0], 0x00000008U);
::location::nearby::connections::LocationHint* released = _impl_.location_hint_;
_impl_.location_hint_ = nullptr;
if (::google::protobuf::internal::DebugHardenForceCopyInRelease()) {
@@ -13598,7 +13616,7 @@ inline ::location::nearby::connections::LocationHint* PROTOBUF_NULLABLE Connecti
::google::protobuf::internal::TSanWrite(&_impl_);
// @@protoc_insertion_point(field_release:location.nearby.connections.ConnectionResponseFrame.location_hint)
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
ClearHasBit(_impl_._has_bits_[0], 0x00000008U);
::location::nearby::connections::LocationHint* temp = _impl_.location_hint_;
_impl_.location_hint_ = nullptr;
return temp;
@@ -13613,7 +13631,7 @@ inline ::location::nearby::connections::LocationHint* PROTOBUF_NONNULL Connectio
}
inline ::location::nearby::connections::LocationHint* PROTOBUF_NONNULL ConnectionResponseFrame::mutable_location_hint()
ABSL_ATTRIBUTE_LIFETIME_BOUND {
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
SetHasBit(_impl_._has_bits_[0], 0x00000008U);
::location::nearby::connections::LocationHint* _msg = _internal_mutable_location_hint();
// @@protoc_insertion_point(field_mutable:location.nearby.connections.ConnectionResponseFrame.location_hint)
return _msg;
@@ -13630,9 +13648,9 @@ inline void ConnectionResponseFrame::set_allocated_location_hint(::location::nea
if (message_arena != submessage_arena) {
value = ::google::protobuf::internal::GetOwnedMessage(message_arena, value, submessage_arena);
}
SetHasBit(_impl_._has_bits_[0], 0x00000004U);
SetHasBit(_impl_._has_bits_[0], 0x00000008U);
} else {
ClearHasBit(_impl_._has_bits_[0], 0x00000004U);
ClearHasBit(_impl_._has_bits_[0], 0x00000008U);
}
_impl_.location_hint_ = reinterpret_cast<::location::nearby::connections::LocationHint*>(value);
@@ -13641,14 +13659,14 @@ inline void ConnectionResponseFrame::set_allocated_location_hint(::location::nea
// optional int32 keep_alive_timeout_millis = 9;
inline bool ConnectionResponseFrame::has_keep_alive_timeout_millis() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000100U);
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000200U);
return value;
}
inline void ConnectionResponseFrame::clear_keep_alive_timeout_millis() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.keep_alive_timeout_millis_ = 0;
ClearHasBit(_impl_._has_bits_[0],
0x00000100U);
0x00000200U);
}
inline ::int32_t ConnectionResponseFrame::keep_alive_timeout_millis() const {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.keep_alive_timeout_millis)
@@ -13656,7 +13674,7 @@ inline ::int32_t ConnectionResponseFrame::keep_alive_timeout_millis() const {
}
inline void ConnectionResponseFrame::set_keep_alive_timeout_millis(::int32_t value) {
_internal_set_keep_alive_timeout_millis(value);
SetHasBit(_impl_._has_bits_[0], 0x00000100U);
SetHasBit(_impl_._has_bits_[0], 0x00000200U);
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.keep_alive_timeout_millis)
}
inline ::int32_t ConnectionResponseFrame::_internal_keep_alive_timeout_millis() const {
@@ -13668,6 +13686,75 @@ inline void ConnectionResponseFrame::_internal_set_keep_alive_timeout_millis(::i
_impl_.keep_alive_timeout_millis_ = value;
}
// optional string wifi_direct_device_name = 10;
inline bool ConnectionResponseFrame::has_wifi_direct_device_name() const {
bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000002U);
return value;
}
inline void ConnectionResponseFrame::clear_wifi_direct_device_name() {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.wifi_direct_device_name_.ClearToEmpty();
ClearHasBit(_impl_._has_bits_[0],
0x00000002U);
}
inline const ::std::string& ConnectionResponseFrame::wifi_direct_device_name() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
// @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.wifi_direct_device_name)
return _internal_wifi_direct_device_name();
}
template <typename Arg_, typename... Args_>
PROTOBUF_ALWAYS_INLINE void ConnectionResponseFrame::set_wifi_direct_device_name(Arg_&& arg, Args_... args) {
::google::protobuf::internal::TSanWrite(&_impl_);
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
_impl_.wifi_direct_device_name_.Set(static_cast<Arg_&&>(arg), args..., GetArena());
// @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.wifi_direct_device_name)
}
inline ::std::string* PROTOBUF_NONNULL ConnectionResponseFrame::mutable_wifi_direct_device_name()
ABSL_ATTRIBUTE_LIFETIME_BOUND {
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
::std::string* _s = _internal_mutable_wifi_direct_device_name();
// @@protoc_insertion_point(field_mutable:location.nearby.connections.ConnectionResponseFrame.wifi_direct_device_name)
return _s;
}
inline const ::std::string& ConnectionResponseFrame::_internal_wifi_direct_device_name() const {
::google::protobuf::internal::TSanRead(&_impl_);
return _impl_.wifi_direct_device_name_.Get();
}
inline void ConnectionResponseFrame::_internal_set_wifi_direct_device_name(const ::std::string& value) {
::google::protobuf::internal::TSanWrite(&_impl_);
_impl_.wifi_direct_device_name_.Set(value, GetArena());
}
inline ::std::string* PROTOBUF_NONNULL ConnectionResponseFrame::_internal_mutable_wifi_direct_device_name() {
::google::protobuf::internal::TSanWrite(&_impl_);
return _impl_.wifi_direct_device_name_.Mutable( GetArena());
}
inline ::std::string* PROTOBUF_NULLABLE ConnectionResponseFrame::release_wifi_direct_device_name() {
::google::protobuf::internal::TSanWrite(&_impl_);
// @@protoc_insertion_point(field_release:location.nearby.connections.ConnectionResponseFrame.wifi_direct_device_name)
if (!CheckHasBit(_impl_._has_bits_[0], 0x00000002U)) {
return nullptr;
}
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
auto* released = _impl_.wifi_direct_device_name_.Release();
if (::google::protobuf::internal::DebugHardenForceCopyDefaultString()) {
_impl_.wifi_direct_device_name_.Set("", GetArena());
}
return released;
}
inline void ConnectionResponseFrame::set_allocated_wifi_direct_device_name(::std::string* PROTOBUF_NULLABLE value) {
::google::protobuf::internal::TSanWrite(&_impl_);
if (value != nullptr) {
SetHasBit(_impl_._has_bits_[0], 0x00000002U);
} else {
ClearHasBit(_impl_._has_bits_[0], 0x00000002U);
}
_impl_.wifi_direct_device_name_.SetAllocated(value, GetArena());
if (::google::protobuf::internal::DebugHardenForceCopyDefaultString() && _impl_.wifi_direct_device_name_.IsDefault()) {
_impl_.wifi_direct_device_name_.Set("", GetArena());
}
// @@protoc_insertion_point(field_set_allocated:location.nearby.connections.ConnectionResponseFrame.wifi_direct_device_name)
}
// -------------------------------------------------------------------
// PayloadTransferFrame_PayloadHeader
+4
View File
@@ -99,6 +99,8 @@ cc_library(
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:mac_address",
"//sharing/internal/base:utf_utils",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
],
@@ -262,6 +264,7 @@ cc_library(
"//internal/platform/implementation:wifi_utils",
"//proto:connections_enums_cc_proto",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/cleanup",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
@@ -349,6 +352,7 @@ cc_test(
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
)
@@ -422,6 +422,15 @@ int BaseEndpointChannel::GetFrequency() const { return frequency_; }
// Returns the try count of this EndpointChannel.
int BaseEndpointChannel::GetTryCount() const { return try_count_; }
void BaseEndpointChannel::SetLocalEndpointId(
const std::string& local_endpoint_id) {
local_endpoint_id_ = local_endpoint_id;
}
std::string BaseEndpointChannel::GetLocalEndpointId() const {
return local_endpoint_id_;
}
int BaseEndpointChannel::GetMaxAllowedReadBytes() const {
int64_t max_allowed_read_bytes = NearbyFlags::GetInstance().GetInt64Flag(
config_package_nearby::nearby_connections_feature::
@@ -81,6 +81,8 @@ class BaseEndpointChannel : public EndpointChannel {
uint32_t GetNextKeepAliveSeqNo() const override;
void SetAnalyticsRecorder(analytics::AnalyticsRecorder* analytics_recorder,
const std::string& endpoint_id) override;
void SetLocalEndpointId(const std::string& local_endpoint_id) override;
std::string GetLocalEndpointId() const override;
// Reads a complete packet from the underlying medium.
virtual ExceptionOr<ByteArray> DispatchPacket() {
@@ -166,6 +168,7 @@ class BaseEndpointChannel : public EndpointChannel {
analytics::AnalyticsRecorder* analytics_recorder_ = nullptr;
std::string endpoint_id_ = "";
std::string local_endpoint_id_ = "";
};
} // namespace nearby::connections
+30 -15
View File
@@ -25,6 +25,7 @@
#include "securegcm/ukey2_handshake.h"
#include "absl/base/thread_annotations.h"
#include "absl/cleanup/cleanup.h"
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/escaping.h"
@@ -94,7 +95,6 @@ using ::location::nearby::connections::ConnectionResponseFrame;
using ::location::nearby::connections::ConnectionsDevice;
using ::location::nearby::connections::MediumMetadata;
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::PresenceDevice;
using ::location::nearby::connections::V1Frame;
using ::location::nearby::proto::connections::OperationResultCode;
@@ -576,15 +576,16 @@ Status BasePcpHandler::WaitForResult(const std::string& method_name,
return result.result();
}
void BasePcpHandler::RunOnPcpHandlerThread(const std::string& name,
bool BasePcpHandler::RunOnPcpHandlerThread(const std::string& name,
Runnable runnable) {
if (closed_.Get()) {
LOG(WARNING) << "Skip to run PCP Handler task " << name
<< " due to PCP Handler is closed";
return;
return false;
}
serial_executor_.Execute(name, std::move(runnable));
return true;
}
EncryptionRunner::ResultListener BasePcpHandler::GetResultListener(
@@ -894,13 +895,19 @@ ConnectionInfo BasePcpHandler::FillConnectionInfo(
connection_info.ap_frequency = wifi_info.ap_frequency;
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch) &&
client->GetLocalOsInfo().type() == OsInfo::APPLE) {
::location::nearby::connections::MediumRole medium_role_info;
medium_role_info.set_support_awdl_publisher(true);
medium_role_info.set_support_awdl_subscriber(true);
medium_role_info.set_support_wifi_hotspot_client(true);
connection_info.medium_role.emplace(medium_role_info);
kEnableDynamicRoleSwitch)) {
LOG(INFO) << "kEnableDynamicRoleSwitch is enabled";
ClientProxy::MediumsAvailability mediums_availability;
mediums_availability.is_wifi_direct_go_available =
mediums_->GetWifiDirect().IsGOAvailable();
mediums_availability.is_wifi_direct_gc_available =
mediums_->GetWifiDirect().IsGCAvailable();
mediums_availability.is_wifi_hotspot_ap_available =
mediums_->GetWifiHotspot().IsAPAvailable();
mediums_availability.is_wifi_hotspot_client_available =
mediums_->GetWifiHotspot().IsClientAvailable();
connection_info.medium_role.emplace(
client->GetLocalMediumRole(mediums_availability));
}
LOG(INFO) << "Query for WIFI information: is_supports_5_ghz="
<< connection_info.supports_5_ghz
@@ -1585,7 +1592,8 @@ Status BasePcpHandler::AcceptConnection(ClientProxy* client,
Exception write_exception =
channel->Write(parser::ForConnectionResponse(
Status::kSuccess, client->GetLocalOsInfo()));
Status::kSuccess, client->GetLocalOsInfo(),
client->GetLocalDeviceName()));
if (!write_exception.Ok()) {
LOG(INFO) << "AcceptConnection: failed to send response: endpoint_id="
<< endpoint_id;
@@ -1646,7 +1654,8 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client,
Exception write_exception =
channel->Write(parser::ForConnectionResponse(
Status::kConnectionRejected, client->GetLocalOsInfo()));
Status::kConnectionRejected, client->GetLocalOsInfo(),
client->GetLocalDeviceName()));
if (!write_exception.Ok()) {
LOG(INFO) << "RejectConnection: failed to send response: endpoint_id="
<< endpoint_id;
@@ -1673,9 +1682,10 @@ void BasePcpHandler::OnIncomingFrame(
OfflineFrame& frame, const std::string& endpoint_id, ClientProxy* client,
location::nearby::proto::connections::Medium medium) {
CountDownLatch latch(1);
RunOnPcpHandlerThread(
bool scheduled = RunOnPcpHandlerThread(
"incoming-frame",
[this, client, endpoint_id, frame, &latch]() RUN_ON_PCP_HANDLER_THREAD() {
absl::Cleanup release_caller = [&latch] { latch.CountDown(); };
LOG(INFO) << "OnConnectionResponse: endpoint_id=" << endpoint_id;
if (client->HasRemoteEndpointResponded(endpoint_id)) {
@@ -1729,9 +1739,14 @@ void BasePcpHandler::OnIncomingFrame(
EvaluateConnectionResult(client, endpoint_id,
/* can_close_immediately= */ true);
latch.CountDown();
if (connection_response.has_wifi_direct_device_name()) {
client->SetRemoteDeviceName(
endpoint_id, connection_response.wifi_direct_device_name());
}
});
WaitForLatch("OnIncomingFrame()", &latch);
if (scheduled) {
WaitForLatch("OnIncomingFrame()", &latch);
}
}
void BasePcpHandler::OnEndpointDisconnect(ClientProxy* client,
@@ -278,7 +278,7 @@ class BasePcpHandler : public PcpHandler,
};
void Shutdown();
void RunOnPcpHandlerThread(const std::string& name, Runnable runnable);
bool RunOnPcpHandlerThread(const std::string& name, Runnable runnable);
BluetoothDevice GetRemoteBluetoothDevice(
MacAddress remote_bluetooth_mac_address);
@@ -455,8 +455,7 @@ class BasePcpHandlerTest
void TearDown() override { env_.Stop(); }
std::unique_ptr<analytics::AnalyticsRecorder> CreateAnalyticsRecorder() {
auto recorder =
std::make_unique<analytics::MockAnalyticsRecorder>();
auto recorder = std::make_unique<analytics::MockAnalyticsRecorder>();
mock_analytics_recorder_ptr_ = recorder.get();
return recorder;
}
@@ -576,10 +575,9 @@ class BasePcpHandlerTest
[channel = channel_a.get()]() { return channel->DoRead(); });
EXPECT_CALL(*channel_a, Write(_))
.WillOnce(Return(Exception{Exception::kSuccess}))
.WillRepeatedly(
[channel = channel_a.get()](absl::string_view data) {
return channel->DoWrite(data);
});
.WillRepeatedly([channel = channel_a.get()](absl::string_view data) {
return channel->DoWrite(data);
});
EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(medium));
EXPECT_CALL(*channel_a, GetLastReadTimestamp)
.WillRepeatedly(Return(absl::Now()));
@@ -588,10 +586,9 @@ class BasePcpHandlerTest
.WillRepeatedly(
[channel = channel_b.get()]() { return channel->DoRead(); });
EXPECT_CALL(*channel_b, Write(_))
.WillRepeatedly(
[channel = channel_b.get()](absl::string_view data) {
return channel->DoWrite(data);
});
.WillRepeatedly([channel = channel_b.get()](absl::string_view data) {
return channel->DoWrite(data);
});
EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(medium));
EXPECT_CALL(*channel_b, GetLastReadTimestamp)
.WillRepeatedly(Return(absl::Now()));
@@ -628,10 +625,9 @@ class BasePcpHandlerTest
.WillRepeatedly(
[channel = channel_b.get()]() { return channel->DoRead(); });
EXPECT_CALL(*channel_b, Write(_))
.WillRepeatedly(
[channel = channel_b.get()](absl::string_view data) {
return channel->DoWrite(data);
});
.WillRepeatedly([channel = channel_b.get()](absl::string_view data) {
return channel->DoWrite(data);
});
EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(medium));
EXPECT_CALL(*channel_b, GetLastReadTimestamp)
.WillRepeatedly(Return(absl::Now()));
@@ -751,16 +747,15 @@ class BasePcpHandlerTest
auto allowed_mediums = pcp_handler->GetDiscoveryMediums(client);
EXPECT_CALL(*pcp_handler, ConnectImpl)
.WillRepeatedly(
[&channel_a, connect_medium](
ClientProxy* client,
MockPcpHandler::DiscoveredEndpoint* endpoint) {
return MockPcpHandler::ConnectImplResult{
.medium = connect_medium,
.status = {Status::kSuccess},
.endpoint_channel = std::move(channel_a),
};
});
.WillRepeatedly([&channel_a, connect_medium](
ClientProxy* client,
MockPcpHandler::DiscoveredEndpoint* endpoint) {
return MockPcpHandler::ConnectImplResult{
.medium = connect_medium,
.status = {Status::kSuccess},
.endpoint_channel = std::move(channel_a),
};
});
for (const auto& discovered_medium : allowed_mediums) {
pcp_handler->OnEndpointFound(
@@ -1610,7 +1605,7 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) {
LOG(INFO) << "Simulating remote accept: id=" << endpoint_id;
OsInfo os_info;
auto frame = parser::FromBytes(
parser::ForConnectionResponse(Status::kSuccess, os_info));
parser::ForConnectionResponse(Status::kSuccess, os_info, "device_name"));
EXPECT_CALL(mock_connection_listener_.bandwidth_changed_cb, Call).Times(1);
pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, client_.get(),
connect_medium);
@@ -1621,6 +1616,48 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) {
env_.Stop();
}
TEST_P(BasePcpHandlerTest, OnIncomingFrameDuplicateFrameDoesNotDeadlock) {
env_.Start();
std::string endpoint_id{"1234"};
Mediums m;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
BwuManager bwu(m, em, ecm, {}, {});
MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu);
StartDiscovery(client_.get(), &pcp_handler);
auto mediums = pcp_handler.GetDiscoveryMediums(client_.get());
auto connect_medium = mediums[mediums.size() - 1];
auto channel_pair = SetupConnection(connect_medium);
auto& channel_a = channel_pair.first;
std::shared_ptr<MockEndpointChannel> channel_b =
std::move(channel_pair.second);
EXPECT_CALL(*channel_a, CloseImpl).Times(1);
EXPECT_CALL(*channel_b, CloseImpl).Times(1);
RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(),
&pcp_handler, connect_medium);
LOG(INFO) << "Attempting to accept connection: id=" << endpoint_id;
EXPECT_CALL(mock_connection_listener_.accepted_cb, Call).Times(1);
EXPECT_CALL(mock_connection_listener_.disconnected_cb, Call)
.Times(AtLeast(0));
EXPECT_EQ(pcp_handler.AcceptConnection(client_.get(), endpoint_id, {}),
Status{Status::kSuccess});
LOG(INFO) << "Simulating remote accept: id=" << endpoint_id;
OsInfo os_info;
auto frame = parser::FromBytes(
parser::ForConnectionResponse(Status::kSuccess, os_info, "device_name"));
EXPECT_CALL(mock_connection_listener_.bandwidth_changed_cb, Call).Times(1);
pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, client_.get(),
connect_medium);
LOG(INFO) << "Simulating duplicate remote accept: id=" << endpoint_id;
pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, client_.get(),
connect_medium);
LOG(INFO) << "Closing connection: id=" << endpoint_id;
channel_b->Close();
bwu.Shutdown();
pcp_handler.DisconnectFromEndpointManager();
env_.Stop();
}
TEST_P(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) {
env_.Start();
std::atomic_int destroyed_flag = 0;
@@ -1798,8 +1835,8 @@ TEST_F(BasePcpHandlerTest, InjectEndpoint) {
EXPECT_CALL(pcp_handler, InjectEndpointImpl(client_.get(), service_id, _))
.WillOnce([&pcp_handler, &endpoint_id](
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
pcp_handler.OnEndpointFound(
client,
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
@@ -1862,8 +1899,8 @@ TEST_F(BasePcpHandlerTest,
::testing::InSequence seq;
EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call)
.WillOnce([id = endpoint_id](const std::string& endpoint_id,
const ByteArray& endpoint_info,
const std::string& service_id) {
const ByteArray& endpoint_info,
const std::string& service_id) {
EXPECT_EQ(endpoint_id, id);
EXPECT_EQ(endpoint_info, ByteArray{"ABCD"});
});
@@ -1875,8 +1912,8 @@ TEST_F(BasePcpHandlerTest,
EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call)
.WillOnce([id = endpoint_id](const std::string& endpoint_id,
const ByteArray& endpoint_info,
const std::string& service_id) {
const ByteArray& endpoint_info,
const std::string& service_id) {
EXPECT_EQ(endpoint_id, id);
EXPECT_EQ(endpoint_info, ByteArray{"ABCDEF"});
});
@@ -1975,8 +2012,8 @@ TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) {
EXPECT_CALL(pcp_handler, InjectEndpointImpl)
.WillOnce([&pcp_handler, &endpoint_id](
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
pcp_handler.OnEndpointFound(
client,
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
@@ -2038,8 +2075,8 @@ TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) {
EXPECT_CALL(pcp_handler, InjectEndpointImpl)
.WillOnce([&pcp_handler, &endpoint_id](
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
pcp_handler.OnEndpointFound(
client,
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
@@ -2103,31 +2140,30 @@ TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) {
bool first_call = true;
EXPECT_CALL(pcp_handler, InjectEndpointImpl)
.Times(2)
.WillRepeatedly(
[&pcp_handler, &endpoint_id, &first_call](
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
ByteArray endpoint_info;
if (first_call) {
endpoint_info = ByteArray("ABCD");
} else {
endpoint_info = ByteArray("ABCDE");
}
first_call = false;
pcp_handler.OnEndpointFound(
client,
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
{
endpoint_id,
endpoint_info,
service_id,
Medium::BLUETOOTH,
WebRtcState::kUndefined,
},
MockContext{nullptr},
}));
return Status{Status::kSuccess};
});
.WillRepeatedly([&pcp_handler, &endpoint_id, &first_call](
ClientProxy* client, const std::string& service_id,
const OutOfBandConnectionMetadata& metadata) {
ByteArray endpoint_info;
if (first_call) {
endpoint_info = ByteArray("ABCD");
} else {
endpoint_info = ByteArray("ABCDE");
}
first_call = false;
pcp_handler.OnEndpointFound(
client,
std::make_shared<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
{
endpoint_id,
endpoint_info,
service_id,
Medium::BLUETOOTH,
WebRtcState::kUndefined,
},
MockContext{nullptr},
}));
return Status{Status::kSuccess};
});
pcp_handler.InjectEndpoint(
client_.get(), service_id,
OutOfBandConnectionMetadata{
@@ -2949,5 +2985,37 @@ TEST_F(BasePcpHandlerTest, TestForceUpdateEndpointIdAdvertisingOption) {
env_.Stop();
}
TEST_P(BasePcpHandlerTest,
FillConnectionInfo_kEnableDynamicRoleSwitch_Enabled) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
Mediums m;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
BwuManager bwu(m, em, ecm, {}, {});
MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu);
ConnectionRequestInfo request_info = {
.endpoint_info = ByteArray("EndpointInfo"),
};
ConnectionOptions connection_options = {};
// Call FillConnectionInfo with dynamic role switch enabled
ConnectionInfo connection_info = pcp_handler.FillConnectionInfo(
client_.get(), request_info, connection_options);
// Verify that medium_role is set (populated) in connection_info!
EXPECT_TRUE(connection_info.medium_role.has_value());
bwu.Shutdown();
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
} // namespace
} // namespace nearby::connections
+160 -8
View File
@@ -36,6 +36,7 @@
#include "connections/implementation/mediums/mediums.h"
#include "connections/implementation/offline_frames.h"
#include "connections/implementation/service_id_constants.h"
#include "connections/strategy.h"
#include "internal/flags/nearby_flags.h"
#include "connections/medium_selector.h"
#include "internal/platform/cancelable_alarm.h"
@@ -311,11 +312,16 @@ void BwuManager::InitiateBwuForEndpoint(ClientProxy* client,
if (is_dynamic_role_switch_enabled_ &&
client->GetMediumRole(endpoint_id).has_value()) {
MediumRole medium_role = client->GetMediumRole(endpoint_id).value();
if (NeedToSwitchRole(client, endpoint_id, proposed_medium, medium_role)) {
auto remote_os_info = client->GetRemoteOsInfo(endpoint_id);
if (NeedToSwitchRole(client, endpoint_id, proposed_medium, medium_role,
remote_os_info.value_or(OsInfo()))) {
if (!channel
->Write(parser::ForBwuPathRequest(
proposed_medium,
client->GetUpgradeMediums(endpoint_id).GetMediums(true),
medium_role))
medium_role,
mediums_->GetWifi().GetCapability().supports_5_ghz))
.Ok()) {
LOG(ERROR) << "BwuManager couldn't complete the upgrade for endpoint "
<< endpoint_id << " to medium "
@@ -585,6 +591,12 @@ void BwuManager::OnBwuNegotiationFrame(
/* record_analytic= */ true,
OperationResultCode::NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE);
break;
case BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_REQUEST:
if (frame.upgrade_path_info().has_upgrade_path_request()) {
ProcessUpgradePathRequest(client, endpoint_id,
frame.upgrade_path_info());
}
break;
case BandwidthUpgradeNegotiationFrame::LAST_WRITE_TO_PRIOR_CHANNEL:
if (!in_progress_upgrades_.contains(endpoint_id)) {
LOG(ERROR) << "Received LAST_WRITE_TO_PRIOR_CHANNEL for endpoint "
@@ -670,7 +682,19 @@ void BwuManager::OnIncomingConnection(
"OfflineFrame on EndpointChannel "
<< channel->GetName();
const std::string& endpoint_id = introduction.endpoint_id();
std::string endpoint_id = introduction.endpoint_id();
if (is_dynamic_role_switch_enabled_ &&
!in_progress_upgrades_.contains(endpoint_id) &&
introduction.has_last_endpoint_id() &&
!introduction.last_endpoint_id().empty()) {
std::string last_endpoint_id = introduction.last_endpoint_id();
if (in_progress_upgrades_.contains(last_endpoint_id)) {
LOG(INFO) << "BwuManager: aliasing endpoint ID " << endpoint_id
<< " to " << last_endpoint_id;
endpoint_id = last_endpoint_id;
}
}
ClientProxy* mapped_client;
const auto item = in_progress_upgrades_.find(endpoint_id);
if (item == in_progress_upgrades_.end()) return;
@@ -724,6 +748,7 @@ void BwuManager::RunOnBwuManagerThread(const std::string& name,
void BwuManager::RunUpgradeProtocol(
ClientProxy* client, const std::string& endpoint_id,
std::unique_ptr<EndpointChannel> new_channel, bool enable_encryption) {
new_channel->SetLocalEndpointId(client->GetLocalEndpointId());
LOG(INFO) << "RunUpgradeProtocol new channel @" << new_channel.get()
<< " name: " << new_channel->GetName() << ", medium: "
<< location::nearby::proto::connections::Medium_Name(
@@ -823,9 +848,11 @@ void BwuManager::ProcessBwuPathAvailableEvent(
abort_bwu = true;
} else {
auto medium_role = client->GetMediumRole(endpoint_id);
auto remote_os_info = client->GetRemoteOsInfo(endpoint_id);
if (medium_role.has_value() &&
!NeedToSwitchRole(client, endpoint_id, upgrade_medium,
medium_role.value())) {
medium_role.value(),
remote_os_info.value_or(OsInfo()))) {
abort_bwu = true;
}
}
@@ -937,8 +964,13 @@ void BwuManager::ProcessBwuPathAvailableEvent(
}
in_progress_upgrades_.emplace(endpoint_id, client);
bool local_supports_disabling =
(client->GetAdvertisingOptions().strategy == Strategy::kP2pPointToPoint ||
client->GetDiscoveryOptions().strategy == Strategy::kP2pPointToPoint);
bool enable_encryption = !upgrade_path_info.supports_disabling_encryption() ||
!local_supports_disabling;
RunUpgradeProtocol(client, endpoint_id, std::move(channel),
!upgrade_path_info.supports_disabling_encryption());
enable_encryption);
}
ErrorOr<std::unique_ptr<EndpointChannel>>
@@ -1032,10 +1064,25 @@ BwuManager::ProcessBwuPathAvailableEventInternal(
// Write the requisite BANDWIDTH_UPGRADE_NEGOTIATION.CLIENT_INTRODUCTION as
// the first OfflineFrame on this new EndpointChannel.
std::string last_local_endpoint_id = client->GetLastLocalEndpointId();
std::shared_ptr<EndpointChannel> previous_channel =
channel_manager_->GetChannelForEndpoint(endpoint_id);
if (previous_channel != nullptr) {
last_local_endpoint_id = previous_channel->GetLocalEndpointId();
}
LOG(INFO) << "BwuManager get last_local_endpoint_id "
<< last_local_endpoint_id << " from "
<< (previous_channel != nullptr ? "endpoint channel"
: "client proxy");
bool local_supports_disabling =
(client->GetAdvertisingOptions().strategy == Strategy::kP2pPointToPoint ||
client->GetDiscoveryOptions().strategy == Strategy::kP2pPointToPoint);
if (!new_channel
->Write(parser::ForBwuIntroduction(
client->GetLocalEndpointId(),
upgrade_path_info.supports_disabling_encryption()))
client->GetLocalEndpointId(), last_local_endpoint_id,
local_supports_disabling &&
upgrade_path_info.supports_disabling_encryption()))
.Ok()) {
// This was never a fully EstablishedConnection, no need to provide a
// closure reason.
@@ -1634,7 +1681,13 @@ void BwuManager::AttemptToRecordBandwidthUpgradeErrorForUnknownEndpoint(
bool BwuManager::NeedToSwitchRole(
ClientProxy* client, const std::string& endpoint_id, Medium medium,
const location::nearby::connections::MediumRole& medium_role) {
const location::nearby::connections::MediumRole& medium_role,
const location::nearby::connections::OsInfo& remote_os_info) {
if (!is_dynamic_role_switch_enabled_) {
return false;
}
// On called by receiver device, check if the sender device can host the
// upgrade medium or not
if (GetLocalOsInfo(client).type() == OsInfo::APPLE) {
switch (medium) {
case Medium::WIFI_HOTSPOT:
@@ -1643,6 +1696,105 @@ bool BwuManager::NeedToSwitchRole(
break;
}
}
// For testing on Windows as a receiver device to request dynamic role switch.
// No need for final check in.
if (GetLocalOsInfo(client).type() == OsInfo::WINDOWS &&
remote_os_info.type() == OsInfo::ANDROID) {
LOG(INFO) << "Local: Windows OS, Remote: Android device detected. "
"WifiDirect NeedToSwitchRole and let Android be GO. "
"medium_role.support_wifi_direct_group_owner(): "
<< medium_role.support_wifi_direct_group_owner();
switch (medium) {
case Medium::WIFI_DIRECT:
return medium_role.support_wifi_direct_group_owner();
default:
break;
}
}
return false;
}
// This feature currently is only used by Android as receiver device to request
// a dynamic role switch to Windows as Wi-Fi Direct GO. So WIFI_DIRECT is the
// preferred medium to upgrade to.
void BwuManager::ProcessUpgradePathRequest(
ClientProxy* client, const std::string& endpoint_id,
const location::nearby::connections::BandwidthUpgradeNegotiationFrame::
UpgradePathInfo& upgrade_path_info) {
if (!is_dynamic_role_switch_enabled_) {
return;
}
LOG(INFO) << "BwuManager: processing incoming UPGRADE_PATH_REQUEST frame for "
"endpoint "
<< endpoint_id;
const auto& request = upgrade_path_info.upgrade_path_request();
std::vector<Medium> upgrade_mediums;
upgrade_mediums.reserve(request.mediums_size());
bool has_wifi_direct = false;
for (auto m : request.mediums()) {
Medium medium = parser::UpgradePathInfoMediumToMedium(
static_cast<BandwidthUpgradeNegotiationFrame::UpgradePathInfo::Medium>(
m));
LOG(INFO) << "BwuManager: UpgradePathRequest medium: "
<< location::nearby::proto::connections::Medium_Name(medium);
upgrade_mediums.push_back(medium);
if (medium == Medium::WIFI_DIRECT) {
has_wifi_direct = true;
}
}
const location::nearby::connections::MediumRole& medium_role =
request.medium_meta_data().medium_role();
LOG(INFO) << "BwuManager: medium_role: " << medium_role.DebugString();
if (CanHost(client, medium_role)) {
Medium medium = ChooseBestUpgradeMedium(endpoint_id, upgrade_mediums);
if (has_wifi_direct) {
medium = Medium::WIFI_DIRECT;
}
LOG(INFO) << "BwuManager: Initiating BWU for endpoint " << endpoint_id
<< " with medium "
<< location::nearby::proto::connections::Medium_Name(medium);
InitiateBwuForEndpoint(client, endpoint_id, medium);
} else {
ProcessUpgradeFailureEvent(
client, endpoint_id, upgrade_path_info,
BandwidthUpgradeResult::REMOTE_CONNECTION_ERROR,
/* record_analytic= */ true,
OperationResultCode::NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE);
}
}
bool BwuManager::CanHost(
ClientProxy* client,
const location::nearby::connections::MediumRole& medium_role) {
if (!is_dynamic_role_switch_enabled_) {
return false;
}
ClientProxy::MediumsAvailability mediums_availability;
mediums_availability.is_wifi_direct_go_available =
mediums_->GetWifiDirect().IsGOAvailable();
mediums_availability.is_wifi_direct_gc_available =
mediums_->GetWifiDirect().IsGCAvailable();
mediums_availability.is_wifi_hotspot_ap_available =
mediums_->GetWifiHotspot().IsAPAvailable();
mediums_availability.is_wifi_hotspot_client_available =
mediums_->GetWifiHotspot().IsClientAvailable();
const location::nearby::connections::MediumRole& local_medium_role =
client->GetLocalMediumRole(mediums_availability);
if ((local_medium_role.support_wifi_direct_group_owner() &&
medium_role.support_wifi_direct_group_client() &&
mediums_->GetWifiDirect().IsGOAvailable()) ||
(local_medium_role.support_wifi_hotspot_host() &&
medium_role.support_wifi_hotspot_client() &&
mediums_->GetWifiHotspot().IsAPAvailable()) ||
(local_medium_role.support_wifi_aware_publisher() &&
medium_role.support_wifi_aware_subscriber())) {
LOG(INFO) << "BwuManager: Can host the upgrade medium.";
return true;
}
LOG(INFO) << "BwuManager: Can't host the upgrade medium.";
return false;
}
+10 -1
View File
@@ -221,7 +221,16 @@ class BwuManager : public EndpointManager::FrameProcessor {
bool NeedToSwitchRole(
ClientProxy* client, const std::string& endpoint_id, Medium medium,
const location::nearby::connections::MediumRole& medium_role);
const location::nearby::connections::MediumRole& medium_role,
const location::nearby::connections::OsInfo& remote_os_info);
void ProcessUpgradePathRequest(
ClientProxy* client, const std::string& endpoint_id,
const location::nearby::connections::BandwidthUpgradeNegotiationFrame::
UpgradePathInfo& upgrade_path_info);
bool CanHost(ClientProxy* client,
const location::nearby::connections::MediumRole& medium_role);
virtual const location::nearby::connections::OsInfo& GetLocalOsInfo(
ClientProxy* client) const;
+569 -195
View File
@@ -21,6 +21,7 @@
#include "gtest/gtest.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/connection_options.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/bwu_handler.h"
@@ -81,12 +82,193 @@ CreateWifiHotspotCredentials() {
return credentials;
}
class BwuManagerTest : public ::testing::Test {
class BwuManagerBaseTest : public ::testing::Test {
protected:
BwuManagerTest() {
void SetUp() override {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableWifiDirect,
true);
}
void TearDown() override {
NearbyFlags::GetInstance().ResetOverridedValues();
}
};
TEST_F(BwuManagerBaseTest, InitiateBwu_NeedToSwitchRole_Success) {
ClientProxy client;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
Mediums mediums;
BwuManager::Config config;
config.allow_upgrade_to.SetAll(false);
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto bwu_manager = std::make_unique<BwuManager>(mediums, em, ecm,
std::move(handlers), config);
client.SetLocalOsType(OsInfo::APPLE);
auto channel1 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
MediumRole medium_role;
medium_role.set_support_wifi_hotspot_host(true);
client.OnConnectionInitiated(
std::string(kEndpointId1),
{.remote_endpoint_info = ByteArray("remote endpoint")},
{.auto_upgrade_bandwidth = false,
.connection_info =
{
.medium_role = {medium_role},
}},
{}, "");
client.OnConnectionAccepted(std::string(kEndpointId1));
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId1),
std::move(channel1));
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId1),
Medium::WIFI_HOTSPOT);
EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1)));
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
bwu_manager->Shutdown();
}
TEST_F(BwuManagerBaseTest,
InitiateBwu_NeedToSwitchRole_WindowsAndroid_Success) {
ClientProxy client;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
Mediums mediums;
BwuManager::Config config;
config.allow_upgrade_to.SetAll(false);
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto bwu_manager = std::make_unique<BwuManager>(mediums, em, ecm,
std::move(handlers), config);
// Set up local as WINDOWS, remote as ANDROID
client.SetLocalOsType(OsInfo::WINDOWS);
OsInfo remote_os_info;
remote_os_info.set_type(OsInfo::ANDROID);
auto channel1 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
auto* channel1_ptr = channel1.get();
MediumRole remote_medium_role;
remote_medium_role.set_support_wifi_direct_group_owner(true);
client.OnConnectionInitiated(
std::string(kEndpointId1),
{.remote_endpoint_info = ByteArray("remote endpoint")},
{.auto_upgrade_bandwidth = false,
.connection_info =
{
.medium_role = {remote_medium_role},
}},
{}, "");
client.OnConnectionAccepted(std::string(kEndpointId1));
client.SetRemoteOsInfo(kEndpointId1, remote_os_info);
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId1),
std::move(channel1));
// Verify that before upgrade, write_timestamp is infinite past
EXPECT_EQ(channel1_ptr->GetLastWriteTimestamp(), absl::InfinitePast());
// Initiate BWU for WiFi Direct on Windows, which forces
// role switch to Android
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId1),
Medium::WIFI_DIRECT);
// Since role is switched, upgrade is NOT initiated locally, but delegated
EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1)));
// Verify that an UPGRADE_PATH_REQUEST frame was actually written to
// the channel
EXPECT_NE(channel1_ptr->GetLastWriteTimestamp(), absl::InfinitePast());
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
bwu_manager->Shutdown();
}
TEST_F(BwuManagerBaseTest,
InitiateBwu_NeedToSwitchRole_WindowsAndroid_NoSwitch_Success) {
ClientProxy client;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
Mediums mediums;
BwuManager::Config config;
config.allow_upgrade_to.SetAll(false);
config.allow_upgrade_to.wifi_direct = true;
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_wifi_direct = std::make_unique<FakeBwuHandler>(Medium::WIFI_DIRECT);
auto* fake_wifi_direct_ptr = fake_wifi_direct.get();
handlers.emplace(Medium::WIFI_DIRECT, std::move(fake_wifi_direct));
auto bwu_manager = std::make_unique<BwuManager>(mediums, em, ecm,
std::move(handlers), config);
// Set up local as WINDOWS, remote as ANDROID
client.SetLocalOsType(OsInfo::WINDOWS);
OsInfo remote_os_info;
remote_os_info.set_type(OsInfo::ANDROID);
auto channel1 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
MediumRole remote_medium_role;
remote_medium_role.set_support_wifi_direct_group_owner(false);
client.OnConnectionInitiated(
std::string(kEndpointId1),
{.remote_endpoint_info = ByteArray("remote endpoint")},
{.auto_upgrade_bandwidth = false,
.connection_info =
{
.medium_role = {remote_medium_role},
}},
{}, "");
client.OnConnectionAccepted(std::string(kEndpointId1));
client.SetRemoteOsInfo(kEndpointId1, remote_os_info);
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId1),
std::move(channel1));
// Verify that before upgrade, upgrade is not ongoing
EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1)));
// Initiate BWU for WiFi Direct on Windows. Since remote doesn't support GO,
// we do not switch roles, so we host/upgrade locally.
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId1),
Medium::WIFI_DIRECT);
// Upgrade is ongoing locally
EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1)));
EXPECT_EQ(fake_wifi_direct_ptr->handle_initialize_calls().size(), 1u);
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
bwu_manager->Shutdown();
}
class BwuManagerTest : public ::testing::Test {
protected:
static void SetUpTestSuite() {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableWifiDirect,
true);
}
static void TearDownTestSuite() {
NearbyFlags::GetInstance().ResetOverridedValues();
}
BwuManagerTest() {
// Set up fake BWU handlers for WebRTC and WifiLAN.
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_web_rtc = std::make_unique<FakeBwuHandler>(Medium::WEB_RTC);
@@ -210,65 +392,50 @@ class BwuManagerTest : public ::testing::Test {
std::unique_ptr<BwuManager> bwu_manager_;
};
TEST(BwuManagerBaseTest, AllowToUpgradeMedium) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableWifiDirect,
true);
ClientProxy client;
EndpointChannelManager ecm;
EndpointManager em(&ecm);
Mediums mediums;
BwuManager::Config config;
config.allow_upgrade_to.SetAll(false);
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto bwu_manager = std::make_unique<BwuManager>(mediums, em, ecm,
std::move(handlers), config);
TEST_F(BwuManagerTest, AllowToUpgradeMedium) {
auto channel1 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId1),
ecm_.RegisterChannelForEndpoint(&client_, std::string(kEndpointId1),
std::move(channel1));
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId1),
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_LAN);
EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1)));
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1),
EXPECT_TRUE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId1)));
ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
auto channel2 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId2),
ecm_.RegisterChannelForEndpoint(&client_, std::string(kEndpointId2),
std::move(channel2));
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId2),
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId2),
Medium::WIFI_HOTSPOT);
EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId2)));
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId2),
EXPECT_TRUE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId2)));
ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
auto channel3 = std::make_unique<FakeEndpointChannel>(
Medium::BLUETOOTH, std::string(kServiceIdA));
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId3),
ecm_.RegisterChannelForEndpoint(&client_, std::string(kEndpointId3),
std::move(channel3));
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId3),
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId3),
Medium::WIFI_DIRECT);
EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId3)));
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId3),
EXPECT_TRUE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId3)));
ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId3),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
auto channel4 = std::make_unique<FakeEndpointChannel>(
Medium::WEB_RTC, std::string(kServiceIdA));
ecm.RegisterChannelForEndpoint(&client, std::string(kEndpointId4),
ecm_.RegisterChannelForEndpoint(&client_, std::string(kEndpointId4),
std::move(channel4));
bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId4),
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId4),
Medium::BLUETOOTH);
EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId4)));
ecm.UnregisterChannelForEndpoint(std::string(kEndpointId4),
EXPECT_FALSE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId4)));
ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId4),
DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
bwu_manager->Shutdown();
}
TEST(BwuManagerBaseTest, GcOnlyDoesNotInitializeWifiDirectAsGO) {
@@ -428,167 +595,6 @@ TEST(BwuManagerBaseTest, InitiateBwu_NeedToSwitchRole_Success) {
false);
}
class BwuManagerTestParam : public BwuManagerTest,
public ::testing::WithParamInterface<bool> {
protected:
BwuManagerTestParam() {
SetSupportMultipleBwuMediums(GetParam());
}
};
TEST_P(BwuManagerTestParam, InitiateBwu_Success) {
// Create the initial device-to-device Bluetooth connection.
FakeEndpointChannel* initial_channel = CreateInitialEndpoint(
&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Initiate BWU, and send BANDWIDTH_UPGRADE_NEGOTIATION.UPGRADE_PATH_AVAILABLE
// to the Responder over the initial Bluetooth channel.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
// The appropriate upgrade medium handler is informed of the BWU initiation.
ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
EXPECT_EQ(WrapInitiatorUpgradeServiceId(kServiceIdA),
fake_web_rtc_bwu_handler_->handle_initialize_calls()[0].service_id);
EXPECT_EQ(
kEndpointId1,
fake_web_rtc_bwu_handler_->handle_initialize_calls()[0].endpoint_id);
// Establish the incoming connection on the new medium. Verify that the
// upgrade channel replaces the initial channel.
std::shared_ptr<EndpointChannel> shared_initial_channel =
ecm_.GetChannelForEndpoint(std::string(kEndpointId1));
EXPECT_EQ(initial_channel, shared_initial_channel.get());
FakeEndpointChannel* upgraded_channel =
fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection(
/*initialize_call_index=*/0u, bwu_manager_.get());
EXPECT_EQ(upgraded_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
// Confirm that upgrade channel is paused until initial channel is shut down.
EXPECT_TRUE(upgraded_channel->IsPaused());
EXPECT_FALSE(initial_channel->is_closed());
// Receive BANDWIDTH_UPGRADE_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL and then
// BANDWIDTH_UPGRADE_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL from the
// Responder device to trigger the shutdown of the initial Bluetooth channel.
ExceptionOr<OfflineFrame> last_write_frame =
parser::FromBytes(parser::ForBwuLastWrite());
bwu_manager_->OnIncomingFrame(last_write_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
ExceptionOr<OfflineFrame> safe_to_close_frame =
parser::FromBytes(parser::ForBwuSafeToClose());
bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
// Confirm that upgrade channel is resumed after initial channel is shut down.
// Note: If we didn't grab the shared initial channel pointer above, this
// channel would have already been destroyed.
auto old_channel =
dynamic_cast<FakeEndpointChannel*>(shared_initial_channel.get());
EXPECT_FALSE(upgraded_channel->IsPaused());
EXPECT_TRUE(old_channel->is_closed());
EXPECT_EQ(location::nearby::proto::connections::DisconnectionReason::UPGRADED,
old_channel->disconnection_reason());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_DontUpgradeIfAlreadyConenctedOverTheRequestedMedium) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
FullyUpgradeEndpoint(kEndpointId1, /*initial_medium=*/Medium::BLUETOOTH,
/*upgrade_medium=*/Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// Ignore request to upgrade to WebRTC if we're already connected.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_DontUpgradeFromWIFI_LANToWIFI_HOTSPOT) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::WIFI_LAN);
// Ignore request to upgrade to WebRTC if we're already connected.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_HOTSPOT);
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam, InitiateBwu_Error_NoInitialMedium) {
// Try to upgrade to a Medium without an initial Medium.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_HOTSPOT);
// Make sure none of the other medium handlers are called.
EXPECT_TRUE(fake_web_rtc_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
}
TEST_P(BwuManagerTestParam, InitiateBwu_Error_UpgradeAlreadyInProgress) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// Try to upgrade an endpoint that already has an ungrade in progress. Should
// just early return with no action.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_LAN);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_FailedToWriteUpgradePathAvailableFrame) {
// Create the initial device-to-device Bluetooth connection.
FakeEndpointChannel* initial_channel = CreateInitialEndpoint(
&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Make the initial endpoint channel fail when writing the
// UPGRADE_PATH_AVAILABLE frame.
initial_channel->set_write_output(Exception{Exception::kIo});
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
// After we notify the WebRTC handler, we try to write the
// UPGRADE_PATH_AVAILABLE frame, but fail by just early returning.
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// However, we do not record an in-progress attempt. So, if we see an incoming
// connection over WebRTC, we ignore it. In other words, the initial BLUETOOTH
// channel is still used.
EXPECT_EQ(initial_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
FakeEndpointChannel* upgraded_channel =
fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection(
/*initialize_call_index=*/0u, bwu_manager_.get());
EXPECT_NE(upgraded_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
EXPECT_EQ(initial_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_F(BwuManagerTest,
InitiateBwu_Revert_OnDisconnect_MultipleEndpoints_FlagEnabled) {
SetSupportMultipleBwuMediums(true);
@@ -1286,6 +1292,374 @@ TEST_F(BwuManagerTest, ReceiveUnexpectedLastWriteBeforeUpgrade_NoWedge) {
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_F(BwuManagerTest, ProcessUpgradePathRequest_CanHost_True) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
// Shutdown original BwuManager to clean up registrations cleanly.
bwu_manager_->Shutdown();
// Set up fake BWU handlers for WifiDirect.
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_wifi_direct =
std::make_unique<FakeBwuHandler>(Medium::WIFI_DIRECT);
FakeBwuHandler* fake_wifi_direct_handler_ptr = fake_wifi_direct.get();
handlers.emplace(Medium::WIFI_DIRECT, std::move(fake_wifi_direct));
BwuManager::Config config;
config.allow_upgrade_to = BooleanMediumSelector{.wifi_direct = true};
bwu_manager_ = std::make_unique<BwuManager>(
mediums_, em_, ecm_, std::move(handlers), config);
bwu_manager_->MakeSingleThreadedForTesting();
// Create initial connection
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Build the UpgradePathRequest frame using the parser helper
location::nearby::connections::MediumRole remote_medium_role;
remote_medium_role.set_support_wifi_direct_group_client(true);
std::string bytes = parser::ForBwuPathRequest(
Medium::WIFI_DIRECT, {Medium::WIFI_DIRECT}, remote_medium_role,
/*supports_5_ghz=*/true);
OfflineFrame frame;
frame.ParseFromString(bytes);
// Process the request
bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
// Verify that WiFi Direct BWU was initiated
EXPECT_EQ(fake_wifi_direct_handler_ptr->handle_initialize_calls().size(), 1u);
UnRegisterChannelForEndpoint(kEndpointId1);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
TEST_F(BwuManagerTest, ProcessUpgradePathRequest_CanHost_False) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
// Shutdown original BwuManager to clean up registrations cleanly.
bwu_manager_->Shutdown();
// Set up fake BWU handlers for WifiDirect.
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_wifi_direct =
std::make_unique<FakeBwuHandler>(Medium::WIFI_DIRECT);
FakeBwuHandler* fake_wifi_direct_handler_ptr = fake_wifi_direct.get();
handlers.emplace(Medium::WIFI_DIRECT, std::move(fake_wifi_direct));
BwuManager::Config config;
config.allow_upgrade_to = BooleanMediumSelector{.wifi_direct = true};
bwu_manager_ = std::make_unique<BwuManager>(
mediums_, em_, ecm_, std::move(handlers), config);
bwu_manager_->MakeSingleThreadedForTesting();
// Create initial connection
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Build the UpgradePathRequest frame where remote doesn't support GC
location::nearby::connections::MediumRole remote_medium_role;
std::string bytes = parser::ForBwuPathRequest(
Medium::WIFI_DIRECT, {Medium::WIFI_DIRECT}, remote_medium_role,
/*supports_5_ghz=*/true);
OfflineFrame frame;
frame.ParseFromString(bytes);
// Process the request
bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
// Verify that WiFi Direct BWU was NOT initiated
EXPECT_EQ(fake_wifi_direct_handler_ptr->handle_initialize_calls().size(), 0u);
UnRegisterChannelForEndpoint(kEndpointId1);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
TEST_F(BwuManagerTest, ProcessUpgradePathRequest_DynamicRoleSwitchDisabled) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
// Shutdown original BwuManager to clean up registrations cleanly.
bwu_manager_->Shutdown();
// Set up fake BWU handlers for WifiDirect.
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_wifi_direct =
std::make_unique<FakeBwuHandler>(Medium::WIFI_DIRECT);
FakeBwuHandler* fake_wifi_direct_handler_ptr = fake_wifi_direct.get();
handlers.emplace(Medium::WIFI_DIRECT, std::move(fake_wifi_direct));
BwuManager::Config config;
config.allow_upgrade_to = BooleanMediumSelector{.wifi_direct = true};
bwu_manager_ = std::make_unique<BwuManager>(
mediums_, em_, ecm_, std::move(handlers), config);
bwu_manager_->MakeSingleThreadedForTesting();
// Create initial connection
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Build the UpgradePathRequest frame where remote supports GC
location::nearby::connections::MediumRole remote_medium_role;
remote_medium_role.set_support_wifi_direct_group_client(true);
std::string bytes = parser::ForBwuPathRequest(
Medium::WIFI_DIRECT, {Medium::WIFI_DIRECT}, remote_medium_role,
/*supports_5_ghz=*/true);
OfflineFrame frame;
frame.ParseFromString(bytes);
// Process the request
bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
// Verify that WiFi Direct BWU was NOT initiated
EXPECT_EQ(fake_wifi_direct_handler_ptr->handle_initialize_calls().size(), 0u);
UnRegisterChannelForEndpoint(kEndpointId1);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
TEST_F(BwuManagerTest, OnIncomingConnection_EndpointAliasesToLastEndpointId) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
// Shutdown original BwuManager to clean up registrations cleanly.
bwu_manager_->Shutdown();
// Create a new BwuManager with the overridden flag. We use WEB_RTC since
// it has a simple, standard BWU flow.
absl::flat_hash_map<Medium, std::unique_ptr<BwuHandler>> handlers;
auto fake_web_rtc = std::make_unique<FakeBwuHandler>(Medium::WEB_RTC);
handlers.emplace(Medium::WEB_RTC, std::move(fake_web_rtc));
BwuManager::Config config;
config.allow_upgrade_to = BooleanMediumSelector{.web_rtc = true};
bwu_manager_ = std::make_unique<BwuManager>(
mediums_, em_, ecm_, std::move(handlers), config);
bwu_manager_->MakeSingleThreadedForTesting();
// Create initial connection with the old endpoint ID "OldEndpoint"
CreateInitialEndpoint(&client_, kServiceIdA, "OldEndpoint",
Medium::BLUETOOTH);
// Initiate upgrade for "OldEndpoint" (inserts into in_progress_upgrades_)
bwu_manager_->InitiateBwuForEndpoint(&client_, "OldEndpoint",
Medium::WEB_RTC);
// Now simulate incoming upgraded connection. Set introduction read output:
// - endpoint_id = "NewEndpoint"
// - last_endpoint_id = "OldEndpoint"
auto upgraded_channel = std::make_unique<FakeEndpointChannel>(
Medium::WEB_RTC, std::string(kServiceIdA));
FakeEndpointChannel* upgraded_channel_raw = upgraded_channel.get();
std::string intro_frame = parser::ForBwuIntroduction(
"NewEndpoint", "OldEndpoint", /*supports_disabling_encryption=*/false);
upgraded_channel->set_read_output(
ExceptionOr<ByteArray>(ByteArray(intro_frame)));
auto connection = std::make_unique<BwuHandler::IncomingSocketConnection>();
connection->channel = std::move(upgraded_channel);
// Invoke OnIncomingConnection
bwu_manager_->InvokeOnIncomingConnectionForTesting(&client_,
std::move(connection));
// Verify that an Ack frame was written to upgraded_channel_raw
EXPECT_NE(upgraded_channel_raw->GetLastWriteTimestamp(),
absl::InfinitePast());
// Clean up
UnRegisterChannelForEndpoint("OldEndpoint");
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
class BwuManagerTestParam : public BwuManagerTest,
public ::testing::WithParamInterface<bool> {
protected:
BwuManagerTestParam() {
SetSupportMultipleBwuMediums(GetParam());
}
};
TEST_P(BwuManagerTestParam, InitiateBwu_Success) {
// Create the initial device-to-device Bluetooth connection.
FakeEndpointChannel* initial_channel = CreateInitialEndpoint(
&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Initiate BWU, and send BANDWIDTH_UPGRADE_NEGOTIATION.UPGRADE_PATH_AVAILABLE
// to the Responder over the initial Bluetooth channel.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
// The appropriate upgrade medium handler is informed of the BWU initiation.
ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
EXPECT_EQ(WrapInitiatorUpgradeServiceId(kServiceIdA),
fake_web_rtc_bwu_handler_->handle_initialize_calls()[0].service_id);
EXPECT_EQ(
kEndpointId1,
fake_web_rtc_bwu_handler_->handle_initialize_calls()[0].endpoint_id);
// Establish the incoming connection on the new medium. Verify that the
// upgrade channel replaces the initial channel.
std::shared_ptr<EndpointChannel> shared_initial_channel =
ecm_.GetChannelForEndpoint(std::string(kEndpointId1));
EXPECT_EQ(initial_channel, shared_initial_channel.get());
FakeEndpointChannel* upgraded_channel =
fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection(
/*initialize_call_index=*/0u, bwu_manager_.get());
EXPECT_EQ(upgraded_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
// Confirm that upgrade channel is paused until initial channel is shut down.
EXPECT_TRUE(upgraded_channel->IsPaused());
EXPECT_FALSE(initial_channel->is_closed());
// Receive BANDWIDTH_UPGRADE_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL and then
// BANDWIDTH_UPGRADE_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL from the
// Responder device to trigger the shutdown of the initial Bluetooth channel.
ExceptionOr<OfflineFrame> last_write_frame =
parser::FromBytes(parser::ForBwuLastWrite());
bwu_manager_->OnIncomingFrame(last_write_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
ExceptionOr<OfflineFrame> safe_to_close_frame =
parser::FromBytes(parser::ForBwuSafeToClose());
bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
// Confirm that upgrade channel is resumed after initial channel is shut down.
// Note: If we didn't grab the shared initial channel pointer above, this
// channel would have already been destroyed.
auto old_channel =
dynamic_cast<FakeEndpointChannel*>(shared_initial_channel.get());
EXPECT_FALSE(upgraded_channel->IsPaused());
EXPECT_TRUE(old_channel->is_closed());
EXPECT_EQ(location::nearby::proto::connections::DisconnectionReason::UPGRADED,
old_channel->disconnection_reason());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_DontUpgradeIfAlreadyConenctedOverTheRequestedMedium) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
FullyUpgradeEndpoint(kEndpointId1, /*initial_medium=*/Medium::BLUETOOTH,
/*upgrade_medium=*/Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// Ignore request to upgrade to WebRTC if we're already connected.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_DontUpgradeFromWIFI_LANToWIFI_HOTSPOT) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::WIFI_LAN);
// Ignore request to upgrade to WebRTC if we're already connected.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_HOTSPOT);
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam, InitiateBwu_Error_NoInitialMedium) {
// Try to upgrade to a Medium without an initial Medium.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_HOTSPOT);
// Make sure none of the other medium handlers are called.
EXPECT_TRUE(fake_web_rtc_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
}
TEST_P(BwuManagerTestParam, InitiateBwu_Error_UpgradeAlreadyInProgress) {
CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// Try to upgrade an endpoint that already has an ungrade in progress. Should
// just early return with no action.
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WIFI_LAN);
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
EXPECT_TRUE(fake_wifi_lan_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(
fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty());
EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty());
UnRegisterChannelForEndpoint(kEndpointId1);
}
TEST_P(BwuManagerTestParam,
InitiateBwu_Error_FailedToWriteUpgradePathAvailableFrame) {
// Create the initial device-to-device Bluetooth connection.
FakeEndpointChannel* initial_channel = CreateInitialEndpoint(
&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH);
// Make the initial endpoint channel fail when writing the
// UPGRADE_PATH_AVAILABLE frame.
initial_channel->set_write_output(Exception{Exception::kIo});
bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1),
Medium::WEB_RTC);
// After we notify the WebRTC handler, we try to write the
// UPGRADE_PATH_AVAILABLE frame, but fail by just early returning.
EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size());
// However, we do not record an in-progress attempt. So, if we see an incoming
// connection over WebRTC, we ignore it. In other words, the initial BLUETOOTH
// channel is still used.
EXPECT_EQ(initial_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
FakeEndpointChannel* upgraded_channel =
fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection(
/*initialize_call_index=*/0u, bwu_manager_.get());
EXPECT_NE(upgraded_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
EXPECT_EQ(initial_channel,
ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get());
UnRegisterChannelForEndpoint(kEndpointId1);
}
INSTANTIATE_TEST_SUITE_P(BwuManagerTestParam, BwuManagerTestParam,
testing::Bool());
+126 -3
View File
@@ -276,6 +276,12 @@ ClientProxy::ClientProxy(std::unique_ptr<AnalyticsRecorder> analytics_recorder)
// Load advertising info from preferences.
LoadClientInfoFromPreferences();
#ifndef NEARBY_CHROMIUM
local_device_name_ = api::ImplementationPlatform::CreateDeviceInfo()
->GetOsDeviceName()
.value_or("");
#endif
if (preferences_manager_ != nullptr) {
app_lifecycle_monitor_ =
api::ImplementationPlatform::CreateAppLifecycleMonitor(
@@ -376,9 +382,10 @@ void ClientProxy::SetBluetoothMacAddress(const std::string& endpoint_id,
std::string ClientProxy::GenerateLocalEndpointId() {
if (!cached_endpoint_id_.empty()) {
if (stable_endpoint_id_mode_) {
if (stable_endpoint_id_mode_ || HasOngoingConnection()) {
LOG(INFO) << "ClientProxy [Local Endpoint Re-using cached "
"endpoint id due to in stable endpoint id mode]: "
"endpoint id due to in stable endpoint id mode or having "
"ongoing connection]: "
"client="
<< GetClientId()
<< "; cached_endpoint_id_=" << cached_endpoint_id_;
@@ -875,6 +882,48 @@ bool ClientProxy::HasOngoingConnection() const {
!GetConnectedEndpoints().empty();
}
bool ClientProxy::HasWifiDirectConnection() const {
MutexLock lock(&mutex_);
for (const auto& entry : connections_) {
if (entry.second.first.connected_medium == Medium::WIFI_DIRECT) {
LOG(INFO) << "ClientProxy [HasWifiDirectConnection]: true";
return true;
}
}
LOG(INFO) << "ClientProxy [HasWifiDirectConnection]: false";
return false;
}
bool ClientProxy::HasWifiHotspotConnection() const {
MutexLock lock(&mutex_);
for (const auto& entry : connections_) {
if (entry.second.first.connected_medium == Medium::WIFI_HOTSPOT) {
return true;
}
}
return false;
}
bool ClientProxy::HasWifiAwareConnection() const {
MutexLock lock(&mutex_);
for (const auto& entry : connections_) {
if (entry.second.first.connected_medium == Medium::WIFI_AWARE) {
return true;
}
}
return false;
}
std::string ClientProxy::GetLastLocalEndpointId() const {
MutexLock lock(&mutex_);
return last_local_endpoint_id_;
}
void ClientProxy::SetLastLocalEndpointId(absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
last_local_endpoint_id_ = std::string(endpoint_id);
}
std::int32_t ClientProxy::GetNumOutgoingConnections() const {
return GetMatchingEndpoints([](const Connection& connection) {
return connection.status == Connection::kConnected &&
@@ -1133,6 +1182,26 @@ void ClientProxy::SetRemoteOsInfo(absl::string_view endpoint_id,
}
}
void ClientProxy::SetRemoteDeviceName(absl::string_view endpoint_id,
absl::string_view device_name) {
MutexLock lock(&mutex_);
ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
item->first.device_name = std::string(device_name);
LOG(INFO) << "ClientProxy [SetRemoteDeviceName]: " << device_name;
}
}
std::string ClientProxy::GetRemoteDeviceName(
absl::string_view endpoint_id) const {
MutexLock lock(&mutex_);
const ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
return item->first.device_name;
}
return "";
}
std::optional<std::int32_t> ClientProxy::GetRemoteSafeToDisconnectVersion(
absl::string_view endpoint_id) const {
MutexLock lock(&mutex_);
@@ -1249,9 +1318,23 @@ void ClientProxy::RemoveAllEndpoints() {
OnSessionComplete();
}
void ClientProxy::ResetLocalEndpointId() {
MutexLock lock(&mutex_);
if (HasOngoingConnection()) {
return;
}
if (!local_endpoint_id_.empty()) {
last_local_endpoint_id_ = local_endpoint_id_;
local_endpoint_id_.clear();
}
}
void ClientProxy::OnSessionComplete() {
MutexLock lock(&mutex_);
if (connections_.empty() && !IsAdvertising()) {
if (!local_endpoint_id_.empty()) {
last_local_endpoint_id_ = local_endpoint_id_;
}
local_endpoint_id_.clear();
analytics_recorder_->LogSession();
@@ -1298,6 +1381,9 @@ void ClientProxy::EnterStableEndpointIdMode() {
<< GetClientId();
stable_endpoint_id_mode_ = true;
if (!IsAdvertising() && !IsDiscovering() && !HasOngoingConnection()) {
ResetLocalEndpointId();
}
}
void ClientProxy::ExitStableEndpointIdMode() {
@@ -1305,6 +1391,7 @@ void ClientProxy::ExitStableEndpointIdMode() {
VLOG(1) << "ClientProxy [ExitStableEndpointIdMode]: client=" << GetClientId();
stable_endpoint_id_mode_ = false;
ResetLocalEndpointId();
ScheduleClearCachedEndpointIdAlarm();
}
@@ -1318,7 +1405,7 @@ void ClientProxy::ScheduleClearCachedEndpointIdAlarm() {
return;
}
if (HasOngoingConnection()) {
if (IsAdvertising() || IsDiscovering() || HasOngoingConnection()) {
VLOG(1) << "ClientProxy [Handle clearing cached endpoint ID "
"during disconnection]: client="
<< GetClientId();
@@ -1431,6 +1518,41 @@ std::optional<MediumRole> ClientProxy::GetMediumRole(
return std::nullopt;
}
location::nearby::connections::MediumRole ClientProxy::GetLocalMediumRole(
const ClientProxy::MediumsAvailability& mediums_availability) const {
location::nearby::connections::MediumRole medium_role;
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch)) {
return medium_role;
}
if (GetLocalOsInfo().type() == OsInfo::APPLE) {
medium_role.set_support_awdl_publisher(true);
medium_role.set_support_awdl_subscriber(true);
// Apple always supports wifi hotspot client role since they can always
// join a hotspot.
medium_role.set_support_wifi_hotspot_client(true);
return medium_role;
}
medium_role.set_support_wifi_direct_group_owner(
mediums_availability.is_wifi_direct_go_available && !IsUsingP2pMedium());
medium_role.set_support_wifi_direct_group_client(
mediums_availability.is_wifi_direct_gc_available);
medium_role.set_support_wifi_hotspot_host(
mediums_availability.is_wifi_hotspot_ap_available && !IsUsingP2pMedium());
medium_role.set_support_wifi_hotspot_client(
mediums_availability.is_wifi_hotspot_client_available);
LOG(INFO) << "medium_role: " << medium_role.DebugString();
return medium_role;
}
bool ClientProxy::IsUsingP2pMedium() const {
return HasWifiDirectConnection() || HasWifiHotspotConnection() ||
HasWifiAwareConnection();
}
std::optional<std::string> ClientProxy::GetEndpointIdForDct() const {
MutexLock lock(&mutex_);
if (dct_endpoint_id_.empty()) {
@@ -1559,6 +1681,7 @@ std::string ClientProxy::Dump() {
? location::nearby::connections::OsInfo::OsType_Name(
it->second.first.os_info->type())
: "unknown")
<< ", (remote device name) " << it->second.first.device_name
<< std::endl;
}
+32 -1
View File
@@ -75,6 +75,9 @@ class ClientProxy final {
std::string GetLocalEndpointId();
std::string GetLocalEndpointInfo() { return local_endpoint_info_; }
std::string GetLocalDeviceName() {
return local_device_name_;
}
// Override the base for received file attachments from a specific endpoint.
// Returns true if the endpoint is found and the path is overridden.
@@ -103,6 +106,9 @@ class ClientProxy final {
// Clears all the runtime state of this client.
void Reset();
// Resets the local endpoint ID and sets the last local endpoint ID.
void ResetLocalEndpointId();
// Marks this client as advertising with the given callbacks.
void StartedAdvertising(
const std::string& service_id, Strategy strategy,
@@ -145,7 +151,6 @@ class ClientProxy final {
MutexLock lock(&mutex_);
local_endpoint_info_ = std::string(endpoint_info);
}
void UpdateAdvertisingOptions(const AdvertisingOptions& advertising_options) {
MutexLock lock(&mutex_);
advertising_options_ = advertising_options;
@@ -210,6 +215,14 @@ class ClientProxy final {
// Returns true if there is at least one connected connection or one pending
// connection.
bool HasOngoingConnection() const;
// Returns true if there is at least one active WiFi Direct connection.
bool HasWifiDirectConnection() const;
// Returns true if there is at least one active WiFi Hotspot connection.
bool HasWifiHotspotConnection() const;
// Returns true if there is at least one active WiFi Aware connection.
bool HasWifiAwareConnection() const;
std::string GetLastLocalEndpointId() const;
void SetLastLocalEndpointId(absl::string_view endpoint_id);
// 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.
@@ -287,6 +300,9 @@ class ClientProxy final {
void SetRemoteOsInfo(
absl::string_view endpoint_id,
const location::nearby::connections::OsInfo& remote_os_info);
void SetRemoteDeviceName(absl::string_view endpoint_id,
absl::string_view device_name);
std::string GetRemoteDeviceName(absl::string_view endpoint_id) const;
void RegisterDeviceProvider(NearbyDeviceProvider* provider) {
external_device_provider_ = provider;
@@ -333,6 +349,18 @@ class ClientProxy final {
std::optional<location::nearby::connections::MediumRole> GetMediumRole(
absl::string_view endpoint_id) const;
struct MediumsAvailability {
bool is_wifi_direct_go_available = false;
bool is_wifi_direct_gc_available = false;
bool is_wifi_hotspot_ap_available = false;
bool is_wifi_hotspot_client_available = false;
};
location::nearby::connections::MediumRole GetLocalMediumRole(
const MediumsAvailability& mediums_availability) const;
bool IsUsingP2pMedium() const;
// Forces client to regenerate a new local endpoint id.
void ClearCachedLocalEndpointId();
@@ -372,6 +400,7 @@ class ClientProxy final {
std::int32_t safe_to_disconnect_version;
std::int32_t remote_multiplex_socket_bitmask;
std::string save_path;
std::string device_name;
};
using ConnectionPair = std::pair<Connection, PayloadListener>;
@@ -441,6 +470,8 @@ class ClientProxy final {
std::int64_t client_id_;
std::string local_endpoint_id_;
std::string local_endpoint_info_;
std::string last_local_endpoint_id_;
std::string local_device_name_;
// If advertising is in stable endpoint ID mode, the endpoint ID is stable
// for 30s after advertising or disconnection. When stable_endpoint_id_mode_
@@ -1345,6 +1345,7 @@ TEST_F(ClientProxyTest, GetRemoteInfoNullWithoutConnections) {
EXPECT_FALSE(client1()
->GetRemoteSafeToDisconnectVersion(advertising_endpoint.id)
.has_value());
EXPECT_EQ(client1()->GetRemoteDeviceName(advertising_endpoint.id), "");
}
TEST_F(ClientProxyTest, SetRemoteInfoCorrect) {
@@ -1365,6 +1366,10 @@ TEST_F(ClientProxyTest, SetRemoteInfoCorrect) {
EXPECT_EQ(
client1()->GetRemoteSafeToDisconnectVersion(advertising_endpoint.id),
nearby_connections_version);
std::string device_name = "device_name";
client1()->SetRemoteDeviceName(advertising_endpoint.id, device_name);
EXPECT_EQ(client1()->GetRemoteDeviceName(advertising_endpoint.id),
device_name);
}
// Test ClientProxy::AddCancellationFlag, where if a flag is already in the map,
@@ -1563,6 +1568,257 @@ TEST_F(ClientProxyTest, GetSavePathDefaultsToEmpty) {
EXPECT_THAT(client1()->GetSavePath(advertising_endpoint.id), IsEmpty());
}
TEST_F(ClientProxyTest, GetLocalMediumRoleFlagDisabled) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
ClientProxy::MediumsAvailability availability;
availability.is_wifi_direct_go_available = true;
availability.is_wifi_direct_gc_available = true;
availability.is_wifi_hotspot_ap_available = true;
availability.is_wifi_hotspot_client_available = true;
location::nearby::connections::MediumRole role =
client1()->GetLocalMediumRole(availability);
EXPECT_FALSE(role.support_awdl_publisher());
EXPECT_FALSE(role.support_awdl_subscriber());
EXPECT_FALSE(role.support_wifi_direct_group_owner());
EXPECT_FALSE(role.support_wifi_direct_group_client());
EXPECT_FALSE(role.support_wifi_hotspot_host());
EXPECT_FALSE(role.support_wifi_hotspot_client());
}
TEST_F(ClientProxyTest, GetLocalMediumRoleAppleOs) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
client1()->SetLocalOsType(location::nearby::connections::OsInfo::APPLE);
ClientProxy::MediumsAvailability availability;
location::nearby::connections::MediumRole role =
client1()->GetLocalMediumRole(availability);
EXPECT_TRUE(role.support_awdl_publisher());
EXPECT_TRUE(role.support_awdl_subscriber());
EXPECT_TRUE(role.support_wifi_hotspot_client());
EXPECT_FALSE(role.support_wifi_direct_group_owner());
EXPECT_FALSE(role.support_wifi_direct_group_client());
EXPECT_FALSE(role.support_wifi_hotspot_host());
}
TEST_F(ClientProxyTest, GetLocalMediumRoleNonAppleOsNoP2pConnection) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
client1()->SetLocalOsType(location::nearby::connections::OsInfo::ANDROID);
ClientProxy::MediumsAvailability availability;
availability.is_wifi_direct_go_available = true;
availability.is_wifi_direct_gc_available = true;
availability.is_wifi_hotspot_ap_available = true;
availability.is_wifi_hotspot_client_available = true;
location::nearby::connections::MediumRole role =
client1()->GetLocalMediumRole(availability);
EXPECT_TRUE(role.support_wifi_direct_group_owner());
EXPECT_TRUE(role.support_wifi_direct_group_client());
EXPECT_TRUE(role.support_wifi_hotspot_host());
EXPECT_TRUE(role.support_wifi_hotspot_client());
}
TEST_F(ClientProxyTest, GetLocalMediumRoleNonAppleOsWithP2pConnection) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
client1()->SetLocalOsType(location::nearby::connections::OsInfo::ANDROID);
// Setup an active P2P connection to make IsUsingP2pMedium() true
Endpoint advertising_endpoint =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), advertising_endpoint);
client1()->OnBandwidthChanged(advertising_endpoint.id, Medium::WIFI_DIRECT);
EXPECT_TRUE(client1()->IsUsingP2pMedium());
ClientProxy::MediumsAvailability availability;
availability.is_wifi_direct_go_available = true;
availability.is_wifi_direct_gc_available = true;
availability.is_wifi_hotspot_ap_available = true;
availability.is_wifi_hotspot_client_available = true;
location::nearby::connections::MediumRole role =
client1()->GetLocalMediumRole(availability);
EXPECT_FALSE(role.support_wifi_direct_group_owner());
EXPECT_TRUE(role.support_wifi_direct_group_client());
EXPECT_FALSE(role.support_wifi_hotspot_host());
EXPECT_TRUE(role.support_wifi_hotspot_client());
}
TEST_F(ClientProxyTest, GetNumIncomingAndOutgoingConnections) {
// Initially no connections
EXPECT_EQ(client1()->GetNumIncomingConnections(), 0);
EXPECT_EQ(client1()->GetNumOutgoingConnections(), 0);
// Set expectation for acceptance callback on step 1
// (which is outgoing based on discovery_connection_info_)
EXPECT_CALL(mock_advertising_connection_.accepted_cb, Call).Times(1);
// Define a complete listener for advertising
ConnectionListener advertising_listener = {
.initiated_cb = mock_advertising_connection_.initiated_cb.AsStdFunction(),
.accepted_cb = mock_advertising_connection_.accepted_cb.AsStdFunction(),
};
// 1. Establish connection 1
Endpoint advertising_endpoint =
StartAdvertising(client1(), advertising_listener);
EXPECT_CALL(mock_advertising_connection_.initiated_cb, Call).Times(1);
client1()->OnConnectionInitiated(
advertising_endpoint.id, discovery_connection_info_, connection_options_,
advertising_listener, "connection_token1");
// Accept local, accept remote, and then OnConnectionAccepted
client1()->LocalEndpointAcceptedConnection(
advertising_endpoint.id,
{
.payload_cb = mock_discovery_payload_.payload_cb.AsStdFunction(),
.payload_progress_cb =
mock_discovery_payload_.payload_progress_cb.AsStdFunction(),
});
client1()->RemoteEndpointAcceptedConnection(advertising_endpoint.id);
client1()->OnConnectionAccepted(advertising_endpoint.id);
// Verify client1 has 0 incoming connections and 1 outgoing connection
EXPECT_EQ(client1()->GetNumIncomingConnections(), 0);
EXPECT_EQ(client1()->GetNumOutgoingConnections(), 1);
// Set expectation for acceptance callback on step 2
// (which is incoming based on advertising_connection_info_)
EXPECT_CALL(mock_discovery_connection_.accepted_cb, Call).Times(1);
// 2. Establish connection 2
StartDiscovery(client1(), GetDiscoveryListener());
Endpoint remote_endpoint = {
.info = ByteArray{"remote endpoint name"},
.id = "rem_ep_id",
};
OnDiscoveryEndpointFound(client1(), remote_endpoint);
EXPECT_CALL(mock_discovery_connection_.initiated_cb, Call).Times(1);
client1()->OnConnectionInitiated(
remote_endpoint.id, advertising_connection_info_, connection_options_,
discovery_connection_listener_, "connection_token2");
// Accept local, accept remote, and then OnConnectionAccepted
client1()->LocalEndpointAcceptedConnection(
remote_endpoint.id,
{
.payload_cb = mock_discovery_payload_.payload_cb.AsStdFunction(),
.payload_progress_cb =
mock_discovery_payload_.payload_progress_cb.AsStdFunction(),
});
client1()->RemoteEndpointAcceptedConnection(remote_endpoint.id);
client1()->OnConnectionAccepted(remote_endpoint.id);
// Verify client1 has 1 incoming connection and 1 outgoing connection
EXPECT_EQ(client1()->GetNumIncomingConnections(), 1);
EXPECT_EQ(client1()->GetNumOutgoingConnections(), 1);
}
TEST_F(ClientProxyTest, IsUsingP2pMediumTests) {
// With no connections, IsUsingP2pMedium should be false
EXPECT_FALSE(client1()->IsUsingP2pMedium());
// 1. Connection with Non-P2P medium (e.g. WIFI_LAN)
Endpoint endpoint_lan =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), endpoint_lan);
client1()->OnBandwidthChanged(endpoint_lan.id, Medium::WIFI_LAN);
EXPECT_FALSE(client1()->IsUsingP2pMedium());
// Clean-up connection
client1()->OnDisconnected(endpoint_lan.id, /*notify=*/false);
EXPECT_FALSE(client1()->IsUsingP2pMedium());
// 2. Connection with WIFI_DIRECT
Endpoint endpoint_direct =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), endpoint_direct);
client1()->OnBandwidthChanged(endpoint_direct.id, Medium::WIFI_DIRECT);
EXPECT_TRUE(client1()->IsUsingP2pMedium());
client1()->OnDisconnected(endpoint_direct.id, /*notify=*/false);
// 3. Connection with WIFI_HOTSPOT
Endpoint endpoint_hotspot =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), endpoint_hotspot);
client1()->OnBandwidthChanged(endpoint_hotspot.id, Medium::WIFI_HOTSPOT);
EXPECT_TRUE(client1()->IsUsingP2pMedium());
client1()->OnDisconnected(endpoint_hotspot.id, /*notify=*/false);
// 4. Connection with WIFI_AWARE
Endpoint endpoint_aware =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), endpoint_aware);
client1()->OnBandwidthChanged(endpoint_aware.id, Medium::WIFI_AWARE);
EXPECT_TRUE(client1()->IsUsingP2pMedium());
client1()->OnDisconnected(endpoint_aware.id, /*notify=*/false);
EXPECT_FALSE(client1()->IsUsingP2pMedium());
}
TEST_F(ClientProxyTest, GetAndSetLastLocalEndpointId) {
EXPECT_TRUE(client1()->GetLastLocalEndpointId().empty());
client1()->SetLastLocalEndpointId("TestEndpointID");
EXPECT_EQ(client1()->GetLastLocalEndpointId(), "TestEndpointID");
}
TEST_F(ClientProxyTest, ResetLocalEndpointId_OngoingConnectionReturnsEarly) {
std::string old_id = client1()->GetLocalEndpointId();
ASSERT_FALSE(old_id.empty());
// Set up an ongoing connection
OnAdvertisingConnectionInitiated(client1(),
{ByteArray("EndpointInfo"), "EndA"});
EXPECT_TRUE(client1()->HasOngoingConnection());
// ResetLocalEndpointId should NOT clear local_endpoint_id
client1()->ResetLocalEndpointId();
EXPECT_EQ(client1()->GetLocalEndpointId(), old_id);
// Terminate connection
client1()->OnDisconnected("EndA", /*notify=*/false);
EXPECT_FALSE(client1()->HasOngoingConnection());
// ResetLocalEndpointId should now successfully clear local_endpoint_id
client1()->ResetLocalEndpointId();
EXPECT_NE(client1()->GetLocalEndpointId(), old_id);
}
TEST_F(ClientProxyTest, ResetLocalEndpointId_SavesToLastLocalEndpointId) {
std::string old_id = client1()->GetLocalEndpointId();
ASSERT_FALSE(old_id.empty());
client1()->ResetLocalEndpointId();
EXPECT_EQ(client1()->GetLastLocalEndpointId(), old_id);
}
TEST_F(ClientProxyTest, OnSessionComplete_SavesToLastLocalEndpointId) {
std::string old_id = client1()->GetLocalEndpointId();
ASSERT_FALSE(old_id.empty());
// Put client into advertising mode first
client1()->StartedAdvertising(service_id_, strategy_, {}, {}, {});
EXPECT_TRUE(client1()->IsAdvertising());
// Stopping advertising triggers OnSessionComplete.
// Since connections_ is empty, it completes the session and should save last
// endpoint ID.
client1()->StoppedAdvertising();
EXPECT_FALSE(client1()->IsAdvertising());
EXPECT_EQ(client1()->GetLastLocalEndpointId(), old_id);
}
} // namespace
} // namespace connections
} // namespace nearby
@@ -103,6 +103,10 @@ class FakeEndpointChannel : public EndpointChannel {
}
void SetAnalyticsRecorder(analytics::AnalyticsRecorder* analytics_recorder,
const std::string& endpoint_id) override {}
void SetLocalEndpointId(const std::string& local_endpoint_id) override {
local_endpoint_id_ = local_endpoint_id;
}
std::string GetLocalEndpointId() const override { return local_endpoint_id_; }
private:
InputStream* in_ = nullptr;
@@ -110,6 +114,7 @@ class FakeEndpointChannel : public EndpointChannel {
absl::Time read_timestamp_ = absl::InfinitePast();
absl::Time write_timestamp_ = absl::InfinitePast();
mutable uint32_t next_keep_alive_seq_no_ = 0;
std::string local_endpoint_id_;
};
struct User {
@@ -128,6 +128,9 @@ class EndpointChannel {
// Enables the multiplex socket on the EndpointChannel.
virtual bool EnableMultiplexSocket() { return false; }
virtual void SetLocalEndpointId(const std::string& local_endpoint_id) = 0;
virtual std::string GetLocalEndpointId() const = 0;
};
inline bool operator==(const EndpointChannel& lhs, const EndpointChannel& rhs) {
@@ -17,7 +17,9 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
@@ -48,7 +50,9 @@ void EndpointChannelManager::RegisterChannelForEndpoint(
LOG(INFO) << "EndpointChannelManager registered channel of type "
<< channel->GetType() << " to endpoint " << endpoint_id;
SetActiveEndpointChannel(client, endpoint_id, std::move(channel),
std::shared_ptr<ChannelState::EndpointData> endpoint =
channel_state_.RegisterEndpoint(endpoint_id);
SetActiveEndpointChannel(client, endpoint_id, endpoint, std::move(channel),
true /* enable_encryption */);
LOG(INFO) << "Registered channel: id=" << endpoint_id;
@@ -58,8 +62,17 @@ void EndpointChannelManager::ReplaceChannelForEndpoint(
ClientProxy* client, const std::string& endpoint_id,
std::shared_ptr<EndpointChannel> channel, bool enable_encryption) {
MutexLock lock(&mutex_);
std::shared_ptr<ChannelState::EndpointData> endpoint =
channel_state_.GetEndpointData(endpoint_id);
if (endpoint == nullptr) {
LOG(WARNING) << "EndpointChannelManager failed to replace channel because "
"endpoint "
<< endpoint_id << " is not registered.";
return;
}
if (client->IsSafeToDisconnectEnabled(endpoint_id) &&
channel_state_.IsWaitingForSafeToDisconnectTimeout(endpoint_id)) {
endpoint->IsWaitingForSafeToDisconnectTimeout()) {
LOG(WARNING)
<< "EndpointChannelManager failed to replace endpoint " << endpoint_id
<< "'s channel with type " << channel->GetType()
@@ -67,13 +80,7 @@ void EndpointChannelManager::ReplaceChannelForEndpoint(
return;
}
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
if (endpoint != nullptr && endpoint->channel == nullptr) {
LOG(INFO) << "EndpointChannelManager is missing channel while "
"trying to update: endpoint "
<< endpoint_id;
}
SetActiveEndpointChannel(client, endpoint_id, std::move(channel),
SetActiveEndpointChannel(client, endpoint_id, endpoint, std::move(channel),
enable_encryption);
}
@@ -82,37 +89,47 @@ bool EndpointChannelManager::EncryptChannelForEndpoint(
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<ChannelState::EndpointData> endpoint =
channel_state_.GetEndpointData(endpoint_id);
if (endpoint == nullptr) {
LOG(WARNING) << "EncryptChannelForEndpoint failed "
<< "because endpoint is not registered: " << endpoint_id;
return false;
}
endpoint->set_context(std::move(context));
return endpoint->EncryptChannel();
}
std::shared_ptr<EndpointChannel> EndpointChannelManager::GetChannelForEndpoint(
const std::string& endpoint_id) {
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
std::shared_ptr<ChannelState::EndpointData> endpoint =
channel_state_.GetEndpointData(endpoint_id);
if (endpoint == nullptr) {
LOG(INFO) << "No channel info for endpoint " << endpoint_id;
return {};
}
return endpoint->channel;
return endpoint->channel();
}
void EndpointChannelManager::SetActiveEndpointChannel(
ClientProxy* client, const std::string& endpoint_id,
std::shared_ptr<ChannelState::EndpointData> endpoint,
std::shared_ptr<EndpointChannel> channel, bool enable_encryption) {
// Update the channel first, then encrypt this new channel, if
// crypto context is present.
channel->SetAnalyticsRecorder(&client->GetAnalyticsRecorder(), endpoint_id);
channel_state_.UpdateChannelForEndpoint(endpoint_id, std::move(channel));
channel_state_.UpdateSafeToDisconnectForEndpoint(
endpoint_id, client->IsSafeToDisconnectEnabled(endpoint_id));
auto* endpoint = channel_state_.LookupEndpointData(endpoint_id);
if (endpoint->IsEncrypted() && enable_encryption)
channel_state_.EncryptChannel(endpoint);
channel->SetLocalEndpointId(client->GetLocalEndpointId());
endpoint->set_channel(std::move(channel));
endpoint->set_safe_to_disconnect_enabled(
client->IsSafeToDisconnectEnabled(endpoint_id));
if (endpoint->IsEncrypted() && enable_encryption) {
endpoint->EncryptChannel();
}
}
int EndpointChannelManager::GetConnectedEndpointsCount() const {
@@ -133,7 +150,7 @@ void EndpointChannelManager::UpdateSafeToDisconnectForEndpoint(
}
void EndpointChannelManager::MarkEndpointStopWaitToDisconnect(
const std::string& endpoint_id, bool is_safe_to_disconnect,
absl::string_view endpoint_id, bool is_safe_to_disconnect,
bool notify_stop_waiting) {
MutexLock lock(&mutex_);
channel_state_.MarkEndpointStopWaitToDisconnect(
@@ -141,61 +158,128 @@ void EndpointChannelManager::MarkEndpointStopWaitToDisconnect(
}
bool EndpointChannelManager::CreateNewTimeoutDisconnectedState(
const std::string& endpoint_id, absl::Duration timeout_millis) {
return channel_state_.CreateNewTimeoutDisconnectedState(endpoint_id,
timeout_millis);
absl::string_view endpoint_id, absl::Duration timeout_millis) {
std::shared_ptr<ChannelState::EndpointData> endpoint_data;
{
MutexLock lock(&mutex_);
endpoint_data = channel_state_.GetEndpointData(endpoint_id);
}
if (!endpoint_data) return false;
LOG(INFO) << "[safe-to-disconnect] "
"Create TimeoutDisconnectedState for endpoint: "
<< endpoint_id;
endpoint_data->CreateNewTimeoutDisconnectedState(timeout_millis);
return true;
}
bool EndpointChannelManager::IsSafeToDisconnect(
const std::string& endpoint_id) {
bool EndpointChannelManager::IsSafeToDisconnect(absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
return channel_state_.IsSafeToDisconnect(endpoint_id);
}
bool EndpointChannelManager::IsWaitingForSafeToDisconnectTimeoutForTesting(
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
return channel_state_.IsWaitingForSafeToDisconnectTimeoutForTesting(
endpoint_id);
}
void EndpointChannelManager::RemoveTimeoutDisconnectedState(
const std::string& endpoint_id) {
absl::string_view endpoint_id) {
MutexLock lock(&mutex_);
channel_state_.RemoveTimeoutDisconnectedState(endpoint_id);
}
///////////////////////////////// ChannelState /////////////////////////////////
// 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);
void EndpointChannelManager::ChannelState::EndpointData::
CreateNewTimeoutDisconnectedState(absl::Duration timeout_millis) {
MutexLock lock(&timeout_to_disconnected_mutex_);
timeout_to_disconnected_enabled_ = true;
timeout_to_disconnected_notified_ = false;
timeout_to_disconnected_.Wait(timeout_millis);
LOG(INFO) << "[safe-to-disconnect] Wait is done with "
<< (timeout_to_disconnected_notified_ ? "notification" : "timeout");
if (!timeout_to_disconnected_notified_) {
is_safe_to_disconnect_ = true;
}
timeout_to_disconnected_notified_ = false;
timeout_to_disconnected_enabled_ = false;
}
void EndpointChannelManager::ChannelState::EndpointData::
MarkEndpointStopWaitToDisconnect(bool is_safe_to_disconnect,
bool notify_stop_waiting) {
MutexLock lock(&timeout_to_disconnected_mutex_);
this->is_safe_to_disconnect_ = is_safe_to_disconnect;
if (!timeout_to_disconnected_enabled_) return;
if (notify_stop_waiting) {
LOG(INFO) << "[safe-to-disconnect] Notify stop waiting before timeout.";
timeout_to_disconnected_.Notify();
timeout_to_disconnected_notified_ = true;
}
}
bool EndpointChannelManager::ChannelState::EndpointData::
IsWaitingForSafeToDisconnectTimeout() const {
MutexLock lock(&timeout_to_disconnected_mutex_);
return timeout_to_disconnected_enabled_;
}
bool EndpointChannelManager::ChannelState::EndpointData::IsSafeToDisconnect()
const {
MutexLock lock(&timeout_to_disconnected_mutex_);
return is_safe_to_disconnect_;
}
void EndpointChannelManager::ChannelState::EndpointData::
RemoveTimeoutDisconnectedState() {
MutexLock lock(&timeout_to_disconnected_mutex_);
timeout_to_disconnected_notified_ = false;
timeout_to_disconnected_enabled_ = false;
}
bool EndpointChannelManager::ChannelState::EndpointData::EncryptChannel() {
if (context_ != nullptr) {
channel_->EnableEncryption(context_);
return true;
}
return false;
}
EndpointChannelManager::ChannelState::EndpointData*
EndpointChannelManager::ChannelState::LookupEndpointData(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
return item != endpoints_.end() ? &item->second : nullptr;
std::shared_ptr<EndpointChannelManager::ChannelState::EndpointData>
EndpointChannelManager::ChannelState::GetEndpointData(
absl::string_view endpoint_id) {
auto it = endpoints_.find(endpoint_id);
return it != endpoints_.end() ? it->second : nullptr;
}
std::shared_ptr<EndpointChannelManager::ChannelState::EndpointData>
EndpointChannelManager::ChannelState::RegisterEndpoint(
absl::string_view endpoint_id) {
std::shared_ptr<EndpointData>& endpoint = endpoints_[endpoint_id];
if (endpoint == nullptr) {
endpoint = std::make_shared<EndpointData>();
} else {
LOG(DFATAL) << "Endpoint " << endpoint_id
<< " is already registered. It might not have been cleaned up "
"properly.";
}
return endpoint;
}
void EndpointChannelManager::ChannelState::DestroyAll() {
for (auto& item : endpoints_) {
RemoveEndpoint(item.first, DisconnectionReason::SHUTDOWN,
/* safe_to_disconnect_enabled */ false,
SafeDisconnectionResult::kSafeDisconnection);
// Collect all endpoint IDs to avoid iterator invalidation.
std::vector<std::string> endpoint_ids;
endpoint_ids.reserve(endpoints_.size());
for (const auto& [endpoint_id, endpoint_data] : endpoints_) {
endpoint_ids.push_back(endpoint_id);
}
endpoints_.clear();
}
void EndpointChannelManager::ChannelState::UpdateChannelForEndpoint(
const std::string& endpoint_id, std::shared_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);
for (const auto& endpoint_id : endpoint_ids) {
RemoveEndpoint(endpoint_id, DisconnectionReason::SHUTDOWN);
}
}
void EndpointChannelManager::ChannelState::UpdateSafeToDisconnectForEndpoint(
@@ -204,32 +288,28 @@ void EndpointChannelManager::ChannelState::UpdateSafeToDisconnectForEndpoint(
"UpdateSafeToDisconnectForEndpoint for: "
<< endpoint_id << " " << safe_to_disconnect_enabled;
endpoints_[endpoint_id].safe_to_disconnect_enabled =
safe_to_disconnect_enabled;
}
bool EndpointChannelManager::ChannelState::GetSafeToDisconnectForEndpoint(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return false;
LOG(INFO) << "[safe-to-disconnect] GetSafeToDisconnectForEndpoint: "
<< item->second.safe_to_disconnect_enabled;
return item->second.safe_to_disconnect_enabled;
std::shared_ptr<EndpointData> endpoint = GetEndpointData(endpoint_id);
if (endpoint == nullptr) {
LOG(WARNING) << "UpdateSafeToDisconnectForEndpoint failed because endpoint "
<< endpoint_id << " is not registered.";
return;
}
endpoint->set_safe_to_disconnect_enabled(safe_to_disconnect_enabled);
}
bool EndpointChannelManager::ChannelState::RemoveEndpoint(
const std::string& endpoint_id, DisconnectionReason reason,
bool safe_to_disconnect_enabled, SafeDisconnectionResult result) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return false;
absl::string_view endpoint_id, DisconnectionReason reason) {
auto it = endpoints_.find(endpoint_id);
if (it == endpoints_.end()) return false;
MarkEndpointStopWaitToDisconnect(endpoint_id,
/* is_safe_to_disconnect */ true,
/* notify_stop_waiting */ true);
item->second.disconnect_reason = reason;
auto channel = item->second.channel;
it->second->set_disconnect_reason(reason);
std::shared_ptr<EndpointChannel> channel = it->second->channel();
bool safe_to_disconnect_enabled = it->second->safe_to_disconnect_enabled();
if (channel && !channel->IsClosed() && !safe_to_disconnect_enabled) {
if (!channel->IsClosed() && !safe_to_disconnect_enabled) {
// If the channel was paused (i.e. during a bandwidth upgrade negotiation)
// we resume to ensure the thread won't hang when trying to write to it.
channel->Resume();
@@ -246,18 +326,16 @@ bool EndpointChannelManager::ChannelState::RemoveEndpoint(
}
LOG(INFO) << "Remove Endpoint: " << endpoint_id;
endpoints_.erase(item);
endpoints_.erase(it);
return true;
}
bool EndpointChannelManager::ChannelState::isWifiLanConnected() const {
for (auto& endpoint : endpoints_) {
auto channel = endpoint.second.channel;
if (channel) {
if (channel->GetMedium() == Medium::WIFI_LAN) {
LOG(INFO) << "Found WIFI_LAN Medium for endpoint:" << endpoint.first;
return true;
}
for (const auto& [endpoint_id, endpoint_data] : endpoints_) {
std::shared_ptr<EndpointChannel> channel = endpoint_data->channel();
if (channel->GetMedium() == Medium::WIFI_LAN) {
LOG(INFO) << "Found WIFI_LAN Medium for endpoint:" << endpoint_id;
return true;
}
}
@@ -265,96 +343,53 @@ bool EndpointChannelManager::ChannelState::isWifiLanConnected() const {
}
void EndpointChannelManager::ChannelState::MarkEndpointStopWaitToDisconnect(
const std::string& endpoint_id, bool is_safe_to_disconnect,
absl::string_view endpoint_id, bool is_safe_to_disconnect,
bool notify_stop_waiting) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return;
std::shared_ptr<EndpointData> endpoint = GetEndpointData(endpoint_id);
if (endpoint == nullptr) return;
LOG(INFO) << "[safe-to-disconnect] is_safe_to_disconnect= "
<< is_safe_to_disconnect
<< ", notify_stop_waiting= " << notify_stop_waiting
<< " for endpoint: " << endpoint_id;
{
MutexLock lock(&item->second.timeout_to_disconnected_mutex);
item->second.is_safe_to_disconnect = is_safe_to_disconnect;
if (!item->second.timeout_to_disconnected_enabled) return;
if (notify_stop_waiting) {
LOG(INFO) << "[safe-to-disconnect] Notify stop "
"waiting before timeout.";
item->second.timeout_to_disconnected.Notify();
item->second.timeout_to_disconnected_notified = true;
}
}
endpoint->MarkEndpointStopWaitToDisconnect(is_safe_to_disconnect,
notify_stop_waiting);
}
bool EndpointChannelManager::ChannelState::CreateNewTimeoutDisconnectedState(
const std::string& endpoint_id, absl::Duration timeout_millis) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return false;
bool EndpointChannelManager::ChannelState::
IsWaitingForSafeToDisconnectTimeoutForTesting(
absl::string_view endpoint_id) {
std::shared_ptr<EndpointData> endpoint = GetEndpointData(endpoint_id);
if (endpoint == nullptr) return false;
bool enabled = endpoint->IsWaitingForSafeToDisconnectTimeout();
LOG(INFO) << "[safe-to-disconnect] "
"Create TimeoutDisconnectedState for endpoint: "
<< endpoint_id;
{
MutexLock lock(&item->second.timeout_to_disconnected_mutex);
item->second.timeout_to_disconnected_enabled = true;
item->second.timeout_to_disconnected_notified = false;
item->second.timeout_to_disconnected.Wait(timeout_millis);
LOG(INFO) << "[safe-to-disconnect] Wait is done with "
<< (item->second.timeout_to_disconnected_notified ? "notification"
: "timeout");
if (!item->second.timeout_to_disconnected_notified)
item->second.is_safe_to_disconnect = true;
item->second.timeout_to_disconnected_notified = false;
item->second.timeout_to_disconnected_enabled = false;
}
return true;
}
bool EndpointChannelManager::ChannelState::IsWaitingForSafeToDisconnectTimeout(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return false;
{
MutexLock lock(&item->second.timeout_to_disconnected_mutex);
LOG(INFO) << "[safe-to-disconnect] "
"IsWaitingForSafeToDisconnectTimeout for endpoint: "
<< endpoint_id << ": "
<< item->second.timeout_to_disconnected_enabled;
return (item->second.timeout_to_disconnected_enabled);
}
"IsWaitingForSafeToDisconnectTimeout for endpoint: "
<< endpoint_id << ": " << enabled;
return enabled;
}
bool EndpointChannelManager::ChannelState::IsSafeToDisconnect(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return true;
{
MutexLock lock(&item->second.timeout_to_disconnected_mutex);
LOG(INFO)
<< "[safe-to-disconnect] Get SafeToDisconnect status for endpoint: "
<< endpoint_id << ": " << item->second.is_safe_to_disconnect;
return (item->second.is_safe_to_disconnect);
}
absl::string_view endpoint_id) {
std::shared_ptr<EndpointData> endpoint = GetEndpointData(endpoint_id);
if (endpoint == nullptr) return true;
bool is_safe = endpoint->IsSafeToDisconnect();
LOG(INFO) << "[safe-to-disconnect] Get SafeToDisconnect status for endpoint: "
<< endpoint_id << ": " << is_safe;
return is_safe;
}
void EndpointChannelManager::ChannelState::RemoveTimeoutDisconnectedState(
const std::string& endpoint_id) {
auto item = endpoints_.find(endpoint_id);
if (item == endpoints_.end()) return;
{
MutexLock lock(&item->second.timeout_to_disconnected_mutex);
item->second.timeout_to_disconnected_notified = false;
item->second.timeout_to_disconnected_enabled = false;
}
absl::string_view endpoint_id) {
std::shared_ptr<EndpointData> endpoint = GetEndpointData(endpoint_id);
if (endpoint == nullptr) return;
endpoint->RemoveTimeoutDisconnectedState();
}
bool EndpointChannelManager::UnregisterChannelForEndpoint(
const std::string& endpoint_id, DisconnectionReason reason,
absl::string_view endpoint_id, DisconnectionReason reason,
SafeDisconnectionResult result) {
MutexLock lock(&mutex_);
auto safe_to_disconnect_enabled =
channel_state_.GetSafeToDisconnectForEndpoint(endpoint_id);
if (!channel_state_.RemoveEndpoint(endpoint_id, reason,
safe_to_disconnect_enabled, result)) {
if (!channel_state_.RemoveEndpoint(endpoint_id, reason)) {
return false;
}
LOG(INFO) << "EndpointChannelManager unregistered channel for endpoint "
@@ -17,9 +17,11 @@
#include <memory>
#include <string>
#include <utility>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/client_proxy.h"
@@ -91,11 +93,11 @@ class EndpointChannelManager final {
// 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_);
absl::string_view 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,
bool UnregisterChannelForEndpoint(absl::string_view endpoint_id,
DisconnectionReason reason,
SafeDisconnectionResult result)
ABSL_LOCKS_EXCLUDED(mutex_);
@@ -107,16 +109,19 @@ class EndpointChannelManager final {
void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id,
bool safe_to_disconnect_enabled)
ABSL_LOCKS_EXCLUDED(mutex_);
void MarkEndpointStopWaitToDisconnect(const std::string& endpoint_id,
bool CreateNewTimeoutDisconnectedState(absl::string_view endpoint_id,
absl::Duration timeout_millis)
ABSL_LOCKS_EXCLUDED(mutex_);
void MarkEndpointStopWaitToDisconnect(absl::string_view endpoint_id,
bool is_safe_to_disconnect,
bool notify_stop_waiting)
ABSL_LOCKS_EXCLUDED(mutex_);
bool CreateNewTimeoutDisconnectedState(const std::string& endpoint_id,
absl::Duration timeout_millis)
bool IsSafeToDisconnect(absl::string_view endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsSafeToDisconnect(const std::string& endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void RemoveTimeoutDisconnectedState(const std::string& endpoint_id)
bool IsWaitingForSafeToDisconnectTimeoutForTesting(
absl::string_view endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_);
void RemoveTimeoutDisconnectedState(absl::string_view endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
@@ -125,32 +130,69 @@ class EndpointChannelManager final {
// been encrypted yet.
class ChannelState {
public:
struct EndpointData {
class EndpointData {
public:
EndpointData() = default;
EndpointData(EndpointData&&) = default;
EndpointData& operator=(EndpointData&&) = default;
~EndpointData() {
if (channel != nullptr) {
channel->Close(disconnect_reason);
if (channel_ != nullptr) {
channel_->Close(disconnect_reason_);
}
}
// True if we have a 'context' for the endpoint.
bool IsEncrypted() const { return context != nullptr; }
bool IsEncrypted() const { return context_ != nullptr; }
std::shared_ptr<EndpointChannel> channel;
std::shared_ptr<EncryptionContext> context;
DisconnectionReason disconnect_reason =
void CreateNewTimeoutDisconnectedState(absl::Duration timeout_millis)
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
void MarkEndpointStopWaitToDisconnect(bool is_safe_to_disconnect,
bool notify_stop_waiting)
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
bool IsWaitingForSafeToDisconnectTimeout() const
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
bool IsSafeToDisconnect() const
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
void RemoveTimeoutDisconnectedState()
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
bool EncryptChannel();
std::shared_ptr<EndpointChannel> channel() const { return channel_; }
void set_channel(std::shared_ptr<EndpointChannel> channel) {
channel_ = std::move(channel);
}
std::shared_ptr<EncryptionContext> context() const { return context_; }
void set_context(std::shared_ptr<EncryptionContext> context) {
context_ = std::move(context);
}
void set_disconnect_reason(DisconnectionReason disconnect_reason) {
disconnect_reason_ = disconnect_reason;
}
bool safe_to_disconnect_enabled() const {
return safe_to_disconnect_enabled_;
}
void set_safe_to_disconnect_enabled(bool safe_to_disconnect_enabled) {
safe_to_disconnect_enabled_ = safe_to_disconnect_enabled;
}
private:
std::shared_ptr<EndpointChannel> channel_;
std::shared_ptr<EncryptionContext> context_;
DisconnectionReason disconnect_reason_ =
DisconnectionReason::UNKNOWN_DISCONNECTION_REASON;
bool safe_to_disconnect_enabled = false;
mutable Mutex timeout_to_disconnected_mutex;
ConditionVariable timeout_to_disconnected{&timeout_to_disconnected_mutex};
bool timeout_to_disconnected_enabled
ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false;
bool timeout_to_disconnected_notified
ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false;
bool is_safe_to_disconnect
ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false;
bool safe_to_disconnect_enabled_ = false;
mutable Mutex timeout_to_disconnected_mutex_;
ConditionVariable timeout_to_disconnected_{
&timeout_to_disconnected_mutex_};
bool timeout_to_disconnected_enabled_
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
bool timeout_to_disconnected_notified_
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
bool is_safe_to_disconnect_
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
};
ChannelState() = default;
@@ -160,57 +202,48 @@ class EndpointChannelManager final {
// Provides a way to destroy contents of a container, while holding a lock.
void DestroyAll();
// Return pointer to endpoint data, or nullptr, it not found.
EndpointData* LookupEndpointData(const std::string& endpoint_id);
std::shared_ptr<EndpointData> GetEndpointData(
absl::string_view endpoint_id);
// Stores a new EndpointChannel for the endpoint.
// Prevoius one is destroyed, if it existed.
void UpdateChannelForEndpoint(const std::string& endpoint_id,
std::shared_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);
// Registers a new endpoint id. This is the only spot EndpointData is
// created.
std::shared_ptr<EndpointData> RegisterEndpoint(
absl::string_view endpoint_id);
void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id,
bool safe_to_disconnect_enabled);
bool GetSafeToDisconnectForEndpoint(const std::string& endpoint_id);
// 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,
DisconnectionReason reason,
bool safe_to_disconnect_enabled,
SafeDisconnectionResult result);
bool RemoveEndpoint(absl::string_view endpoint_id,
DisconnectionReason reason);
bool EncryptChannel(EndpointData* endpoint);
int GetConnectedEndpointsCount() const { return endpoints_.size(); }
bool isWifiLanConnected() const;
void MarkEndpointStopWaitToDisconnect(const std::string& endpoint_id,
void MarkEndpointStopWaitToDisconnect(absl::string_view endpoint_id,
bool is_safe_to_disconnect,
bool notify_stop_waiting);
bool CreateNewTimeoutDisconnectedState(const std::string& endpoint_id,
absl::Duration timeout_millis);
bool IsWaitingForSafeToDisconnectTimeout(const std::string& endpoint_id);
bool IsSafeToDisconnect(const std::string& endpoint_id);
void RemoveTimeoutDisconnectedState(const std::string& endpoint_id);
bool IsWaitingForSafeToDisconnectTimeoutForTesting(
absl::string_view endpoint_id);
bool IsSafeToDisconnect(absl::string_view endpoint_id);
void RemoveTimeoutDisconnectedState(absl::string_view endpoint_id);
private:
// Endpoint ID -> EndpointData. Contains everything we know about the
// endpoint.
absl::flat_hash_map<std::string, EndpointData> endpoints_;
absl::flat_hash_map<std::string, std::shared_ptr<EndpointData>> endpoints_;
};
void SetActiveEndpointChannel(ClientProxy* client,
const std::string& endpoint_id,
std::shared_ptr<EndpointChannel> channel,
bool enable_encryption)
void SetActiveEndpointChannel(
ClientProxy* client, const std::string& endpoint_id,
std::shared_ptr<ChannelState::EndpointData> endpoint,
std::shared_ptr<EndpointChannel> channel, bool enable_encryption)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable Mutex mutex_;
ChannelState channel_state_;
ChannelState channel_state_ ABSL_GUARDED_BY(mutex_);
};
} // namespace nearby::connections
@@ -26,6 +26,7 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/base_endpoint_channel.h"
@@ -210,16 +211,16 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) {
ASSERT_NE(context.second, nullptr);
EndpointChannelManager ecm_a;
ecm_a.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.first));
ecm_a.RegisterChannelForEndpoint(&proxy_a, std::string(kEndpointId),
std::move(channel_a));
ecm_a.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.first));
EndpointChannelManager ecm_b;
ecm_b.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.second));
ecm_b.RegisterChannelForEndpoint(&proxy_b, std::string(kEndpointId),
std::move(channel_b));
ecm_b.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.second));
EXPECT_EQ(channel_a_raw->GetType(), "ENCRYPTED_BLUETOOTH");
EXPECT_EQ(channel_b_raw->GetType(), "ENCRYPTED_BLUETOOTH");
@@ -241,10 +242,10 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) {
channel_a_raw->Close(DisconnectionReason::LOCAL_DISCONNECTION);
channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION);
ecm_a.UnregisterChannelForEndpoint(
std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION,
kEndpointId, DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
ecm_b.UnregisterChannelForEndpoint(
std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION,
kEndpointId, DisconnectionReason::REMOTE_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
}
@@ -290,13 +291,26 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) {
ASSERT_NE(context.first, nullptr);
ASSERT_NE(context.second, nullptr);
auto client_a_dummy = CreatePipe();
auto server_a_dummy = CreatePipe();
auto channel_a_init = std::make_shared<MockEndpointChannel>(
server_a_dummy.first.get(), client_a_dummy.second.get());
auto client_b_dummy = CreatePipe();
auto server_b_dummy = CreatePipe();
auto channel_b_init = std::make_shared<MockEndpointChannel>(
server_b_dummy.first.get(), client_b_dummy.second.get());
EndpointChannelManager ecm_a;
ecm_a.RegisterChannelForEndpoint(&proxy_a, std::string(kEndpointId),
std::move(channel_a_init));
ecm_a.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.first));
ecm_a.ReplaceChannelForEndpoint(&proxy_a, std::string(kEndpointId),
std::move(channel_a), false);
EndpointChannelManager ecm_b;
ecm_b.RegisterChannelForEndpoint(&proxy_b, std::string(kEndpointId),
std::move(channel_b_init));
ecm_b.EncryptChannelForEndpoint(std::string(kEndpointId),
std::move(context.second));
ecm_b.ReplaceChannelForEndpoint(&proxy_b, std::string(kEndpointId),
@@ -309,12 +323,75 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) {
channel_a_raw->Close(DisconnectionReason::LOCAL_DISCONNECTION);
channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION);
ecm_a.UnregisterChannelForEndpoint(
std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION,
kEndpointId, DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
ecm_b.UnregisterChannelForEndpoint(
std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION,
kEndpointId, DisconnectionReason::REMOTE_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
}
TEST(BaseEndpointChannelManagerTest,
CreateNewTimeoutDisconnectedStateUnregisterDuringWait) {
ClientProxy proxy;
EndpointChannelManager ecm;
auto client = CreatePipe();
auto server = CreatePipe();
auto channel = std::make_shared<MockEndpointChannel>(server.first.get(),
client.second.get());
auto channel_raw = channel.get();
ON_CALL(*channel_raw, GetMedium).WillByDefault([]() {
return Medium::BLUETOOTH;
});
ecm.RegisterChannelForEndpoint(&proxy, std::string(kEndpointId),
std::move(channel));
EXPECT_EQ(ecm.GetConnectedEndpointsCount(), 1);
MultiThreadExecutor executor(1);
CountDownLatch start_latch(1);
CountDownLatch finish_latch(1);
bool wait_result = false;
executor.Execute([&]() {
start_latch.CountDown();
wait_result =
ecm.CreateNewTimeoutDisconnectedState(kEndpointId, absl::Seconds(5));
finish_latch.CountDown();
});
ASSERT_TRUE(start_latch.Await(absl::Seconds(1)).result());
// Wait for the endpoint to enter the waiting state.
absl::Time deadline = absl::Now() + absl::Seconds(1);
while (!ecm.IsWaitingForSafeToDisconnectTimeoutForTesting(kEndpointId)) {
ASSERT_TRUE(absl::Now() < deadline)
<< "Timed out waiting for endpoint to enter wait state.";
absl::SleepFor(absl::Milliseconds(10));
}
// Close the channel first to prevent UnregisterChannelForEndpoint from
// attempting to write disconnection frames to it, bypassing the 500ms data
// transfer delay and potential segfaults.
channel_raw->Close(DisconnectionReason::LOCAL_DISCONNECTION);
bool unregister_result = ecm.UnregisterChannelForEndpoint(
kEndpointId, DisconnectionReason::LOCAL_DISCONNECTION,
SafeDisconnectionResult::kSafeDisconnection);
EXPECT_TRUE(unregister_result);
EXPECT_TRUE(finish_latch.Await(absl::Seconds(2)).result());
EXPECT_TRUE(wait_result);
EXPECT_EQ(ecm.GetConnectedEndpointsCount(), 0);
}
TEST(BaseEndpointChannelManagerTest,
CreateNewTimeoutDisconnectedStateReturnsFalseForNonexistentEndpoint) {
EndpointChannelManager ecm;
EXPECT_FALSE(ecm.CreateNewTimeoutDisconnectedState("NonexistentEndpoint",
absl::Seconds(1)));
}
} // namespace
} // namespace nearby::connections
+48 -19
View File
@@ -28,11 +28,13 @@
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/endpoint_channel_manager.h"
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include "connections/implementation/offline_frames.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
#include "connections/implementation/service_id_constants.h"
#include "connections/listeners.h"
#include "connections/medium_selector.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
@@ -64,6 +66,15 @@ constexpr absl::Time kInvalidTimestamp = absl::InfinitePast();
// The maximum time we will wait for the encryption setup during negotiating a
// connection.
constexpr absl::Duration kDecryptRetryTimeout = absl::Seconds(3);
// Returns true if the given `frame_type` is allowed before the connection to
// the endpoint is confirmed (i.e., KEEP_ALIVE, CONNECTION_RESPONSE, and
// DISCONNECTION frames).
bool IsAllowedPreConfirmationFrameType(V1Frame::FrameType frame_type) {
return frame_type == V1Frame::KEEP_ALIVE ||
frame_type == V1Frame::CONNECTION_RESPONSE ||
frame_type == V1Frame::DISCONNECTION;
}
} // namespace
class EndpointManager::LockedFrameProcessor {
@@ -112,7 +123,8 @@ class EndpointManager::LockedFrameProcessor {
void EndpointManager::EndpointChannelLoopRunnable(
const std::string& runnable_name, ClientProxy* client,
const std::string& endpoint_id,
absl::AnyInvocable<ExceptionOr<bool>(EndpointChannel*)> handler) {
absl::AnyInvocable<ExceptionOr<bool>(std::shared_ptr<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.
@@ -143,7 +155,7 @@ void EndpointManager::EndpointChannelLoopRunnable(
break;
}
ExceptionOr<bool> keep_using_channel = handler(channel.get());
ExceptionOr<bool> keep_using_channel = handler(channel);
if (!keep_using_channel.ok()) {
Exception exception = keep_using_channel.GetException();
@@ -195,7 +207,7 @@ void EndpointManager::EndpointChannelLoopRunnable(
}
ExceptionOr<OfflineFrame> EndpointManager::TryDecryptFrame(
const ByteArray& data, EndpointChannel* endpoint_channel) {
const ByteArray& data, std::shared_ptr<EndpointChannel> endpoint_channel) {
auto start_time = SystemClock::ElapsedRealtime();
while (true) {
ExceptionOr<ByteArray> decrypted = endpoint_channel->TryDecrypt(data);
@@ -222,7 +234,7 @@ ExceptionOr<OfflineFrame> EndpointManager::TryDecryptFrame(
ExceptionOr<bool> EndpointManager::HandleData(
const std::string& endpoint_id, ClientProxy* client,
EndpointChannel* endpoint_channel) {
std::shared_ptr<EndpointChannel> endpoint_channel) {
bool try_decrypting = !endpoint_channel->IsEncrypted();
// 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
@@ -274,8 +286,23 @@ ExceptionOr<bool> EndpointManager::HandleData(
// Route the incoming offlineFrame to its registered processor.
V1Frame::FrameType frame_type = parser::GetFrameType(frame);
LockedFrameProcessor frame_processor = GetFrameProcessor(frame_type);
if (!frame_processor) {
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kFilterUnconfirmedEndpointFrames) &&
client->HasPendingConnectionToEndpoint(endpoint_id) &&
!IsAllowedPreConfirmationFrameType(frame_type)) {
LOG(WARNING) << "EndpointManager discarded unauthorized frame ("
<< V1Frame::FrameType_Name(frame_type)
<< ") from unconfirmed endpoint " << endpoint_id << ".";
continue;
}
FrameProcessor* processor = nullptr;
{
LockedFrameProcessor frame_processor = GetFrameProcessor(frame_type);
processor = frame_processor.get();
}
if (!processor) {
// report messages without handlers, except KEEP_ALIVE, which has
// no explicit handler.
if (frame_type == V1Frame::KEEP_ALIVE) {
@@ -310,14 +337,14 @@ ExceptionOr<bool> EndpointManager::HandleData(
continue;
}
frame_processor->OnIncomingFrame(frame, endpoint_id, client,
endpoint_channel->GetMedium());
processor->OnIncomingFrame(frame, endpoint_id, client,
endpoint_channel->GetMedium());
}
}
void EndpointManager::ProcessDisconnectionFrame(
ClientProxy* client, const std::string& endpoint_id,
EndpointChannel* endpoint_channel, OfflineFrame& frame) {
std::shared_ptr<EndpointChannel> endpoint_channel, OfflineFrame& frame) {
if (!client->IsSafeToDisconnectEnabled(endpoint_id)) {
LOG(INFO) << "EndpointManager received a DISCONNECTION frame from endpoint "
<< endpoint_id << " on channel " << endpoint_channel->GetType()
@@ -371,9 +398,9 @@ void EndpointManager::ProcessDisconnectionFrame(
}
ExceptionOr<bool> EndpointManager::HandleKeepAlive(
EndpointChannel* endpoint_channel, absl::Duration keep_alive_interval,
absl::Duration keep_alive_timeout, Mutex* keep_alive_waiter_mutex,
ConditionVariable* keep_alive_waiter) {
std::shared_ptr<EndpointChannel> endpoint_channel,
absl::Duration keep_alive_interval, absl::Duration keep_alive_timeout,
Mutex* keep_alive_waiter_mutex, ConditionVariable* keep_alive_waiter) {
// Check if it has been too long since we received a frame from our endpoint.
absl::Time last_read_time = endpoint_channel->GetLastReadTimestamp();
absl::Duration duration_until_timeout =
@@ -578,7 +605,8 @@ void EndpointManager::RegisterEndpoint(
endpoint_state.StartEndpointReader([this, client, endpoint_id]() {
EndpointChannelLoopRunnable(
"Read", client, endpoint_id,
[this, client, endpoint_id](EndpointChannel* channel) {
[this, client,
endpoint_id](std::shared_ptr<EndpointChannel> channel) {
return HandleData(endpoint_id, client, channel);
});
});
@@ -605,7 +633,7 @@ void EndpointManager::RegisterEndpoint(
"KeepAliveManager", client, endpoint_id,
[this, keep_alive_interval, keep_alive_timeout,
keep_alive_waiter_mutex,
keep_alive_waiter](EndpointChannel* channel) {
keep_alive_waiter](std::shared_ptr<EndpointChannel> channel) {
return HandleKeepAlive(
channel, keep_alive_interval, keep_alive_timeout,
keep_alive_waiter_mutex, keep_alive_waiter);
@@ -745,8 +773,8 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client,
SafeDisconnectionResult::kSafeDisconnection;
// Grab the service ID before we destroy the channel.
EndpointChannel* channel =
channel_manager_->GetChannelForEndpoint(endpoint_id).get();
std::shared_ptr<EndpointChannel> channel =
channel_manager_->GetChannelForEndpoint(endpoint_id);
std::string service_id =
channel ? channel->GetServiceId() : std::string(kUnknownServiceId);
@@ -784,9 +812,10 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client,
RemoveEndpointState(endpoint_id);
}
bool EndpointManager::ApplySafeToDisconnect(const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
DisconnectionReason reason) {
bool EndpointManager::ApplySafeToDisconnect(
const std::string& endpoint_id,
std::shared_ptr<EndpointChannel> endpoint_channel,
DisconnectionReason reason) {
LOG(INFO) << "[safe-to-disconnect] ApplySafeToDisconnect reason: " << reason;
// TODO(b/303544913): clean up the safe-to-disconnect logic
bool is_safe_disconnection = false;
+13 -13
View File
@@ -229,15 +229,14 @@ class EndpointManager {
LockedFrameProcessor GetFrameProcessor(
location::nearby::connections::V1Frame::FrameType frame_type);
ExceptionOr<bool> HandleData(const std::string& endpoint_id,
ClientProxy* client_proxy,
EndpointChannel* endpoint_channel);
ExceptionOr<bool> HandleData(
const std::string& endpoint_id, ClientProxy* client_proxy,
std::shared_ptr<EndpointChannel> endpoint_channel);
ExceptionOr<bool> HandleKeepAlive(EndpointChannel* endpoint_channel,
absl::Duration keep_alive_interval,
absl::Duration keep_alive_timeout,
Mutex* keep_alive_waiter_mutex,
ConditionVariable* keep_alive_waiter);
ExceptionOr<bool> HandleKeepAlive(
std::shared_ptr<EndpointChannel> endpoint_channel,
absl::Duration keep_alive_interval, absl::Duration keep_alive_timeout,
Mutex* keep_alive_waiter_mutex, ConditionVariable* keep_alive_waiter);
// Waits for a given endpoint EndpointChannelLoopRunnable() workers to
// terminate.
@@ -249,7 +248,8 @@ class EndpointManager {
void EndpointChannelLoopRunnable(
const std::string& runnable_name, ClientProxy* client_proxy,
const std::string& endpoint_id,
absl::AnyInvocable<ExceptionOr<bool>(EndpointChannel*)> handler);
absl::AnyInvocable<ExceptionOr<bool>(std::shared_ptr<EndpointChannel>)>
handler);
static void WaitForLatch(const std::string& method_name,
CountDownLatch* latch);
@@ -265,7 +265,7 @@ class EndpointManager {
void RemoveEndpoint(ClientProxy* client, const std::string& endpoint_id,
bool notify, DisconnectionReason reason);
bool ApplySafeToDisconnect(const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
std::shared_ptr<EndpointChannel> endpoint_channel,
DisconnectionReason reason);
void WaitForEndpointDisconnectionProcessing(ClientProxy* client,
const std::string& service_id,
@@ -273,7 +273,7 @@ class EndpointManager {
DisconnectionReason reason);
void ProcessDisconnectionFrame(
ClientProxy* client, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
std::shared_ptr<EndpointChannel> endpoint_channel,
location::nearby::connections::OfflineFrame& frame);
CountDownLatch NotifyFrameProcessorsOnEndpointDisconnect(
ClientProxy* client, const std::string& service_id,
@@ -287,8 +287,8 @@ class EndpointManager {
// Executes all jobs sequentially, on a serial_executor_.
void RunOnEndpointManagerThread(const std::string& name, Runnable runnable);
ExceptionOr<OfflineFrame> TryDecryptFrame(const ByteArray& data,
EndpointChannel* endpoint_channel);
ExceptionOr<OfflineFrame> TryDecryptFrame(
const ByteArray& data, std::shared_ptr<EndpointChannel> endpoint_channel);
EndpointChannelManager* channel_manager_;
RecursiveMutex frame_processors_lock_;
@@ -94,6 +94,10 @@ class SetSafeToDisconnect {
config_package_nearby::nearby_connections_feature::
kSafeToDisconnectVersion,
safe_to_disconnect_version);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kFilterUnconfirmedEndpointFrames,
false);
}
};
@@ -108,11 +112,11 @@ class EndpointManagerTest : public ::testing::Test {
protected:
void RegisterEndpoint(std::unique_ptr<MockEndpointChannel> channel,
bool should_close = true) {
CountDownLatch done(1);
auto done = std::make_shared<CountDownLatch>(1);
if (should_close) {
ON_CALL(*channel, Close(_))
.WillByDefault(
[&done](DisconnectionReason reason) { done.CountDown(); });
[done](DisconnectionReason reason) { done->CountDown(); });
}
EXPECT_CALL(*channel, GetMedium()).WillRepeatedly(Return(Medium::BLE));
EXPECT_CALL(*channel, GetLastReadTimestamp())
@@ -124,7 +128,7 @@ class EndpointManagerTest : public ::testing::Test {
connection_options_, std::move(channel), listener_,
connection_token_);
if (should_close) {
EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(done->Await(absl::Milliseconds(1000)).result());
}
}
SetSafeToDisconnect set_safe_to_disconnect_{true, true, 5};
@@ -429,6 +433,70 @@ TEST_F(EndpointManagerTest, TryDecrypt) {
RegisterEndpoint(std::move(endpoint_channel));
}
TEST_F(EndpointManagerTest,
FilterUnconfirmedFrames_DiscardsPayloadBeforeConfirmation) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kFilterUnconfirmedEndpointFrames,
true);
PayloadTransferFrame::PayloadHeader header;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::BYTES);
header.set_total_size(1024);
PayloadTransferFrame::PayloadChunk chunk;
chunk.set_body("payload data");
chunk.set_offset(150);
chunk.set_flags(1);
std::string payload_bytes = parser::ForDataPayloadTransfer(header, chunk);
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
auto payload_processor = std::make_unique<MockFrameProcessor>();
EXPECT_CALL(*payload_processor, OnIncomingFrame).Times(0);
EXPECT_CALL(*payload_processor, OnEndpointDisconnect);
EXPECT_CALL(*endpoint_channel, Read())
.WillOnce(Return(ExceptionOr<ByteArray>(ByteArray(payload_bytes))))
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Write(_))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
em_.RegisterFrameProcessor(V1Frame::PAYLOAD_TRANSFER,
payload_processor.get());
processors_.emplace_back(std::move(payload_processor));
RegisterEndpoint(std::move(endpoint_channel));
}
TEST_F(EndpointManagerTest,
FilterUnconfirmedFrames_AllowsPayloadAfterConfirmation) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kFilterUnconfirmedEndpointFrames,
true);
PayloadTransferFrame::PayloadHeader header;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::BYTES);
header.set_total_size(1024);
PayloadTransferFrame::PayloadChunk chunk;
chunk.set_body("payload data");
chunk.set_offset(150);
chunk.set_flags(1);
std::string payload_bytes = parser::ForDataPayloadTransfer(header, chunk);
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
auto payload_processor = std::make_unique<MockFrameProcessor>();
EXPECT_CALL(mock_listener_.accepted_cb, Call).Times(1);
EXPECT_CALL(*payload_processor, OnIncomingFrame).Times(1);
EXPECT_CALL(*payload_processor, OnEndpointDisconnect);
EXPECT_CALL(*endpoint_channel, Read())
.WillOnce([this, payload_bytes]() {
client_->OnConnectionAccepted(endpoint_id_);
return ExceptionOr<ByteArray>(ByteArray(payload_bytes));
})
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Write(_))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
em_.RegisterFrameProcessor(V1Frame::PAYLOAD_TRANSFER,
payload_processor.get());
processors_.emplace_back(std::move(payload_processor));
RegisterEndpoint(std::move(endpoint_channel));
}
// Regression test for b/278729669.
//
// During the destruction of NearbyConnections, Core (which owns ClientProxy)
@@ -89,6 +89,7 @@ class FakeBwuHandler : public BaseBwuHandler {
upgraded_channel->set_read_output(
ExceptionOr<ByteArray>(ByteArray(parser::ForBwuIntroduction(
*handle_initialize_calls_[initialize_call_index].endpoint_id,
/*last_endpoint_id=*/"",
false /* supports_disabling_encryption */))));
auto connection = std::make_unique<IncomingSocketConnection>();
connection->channel = std::move(upgraded_channel);
@@ -93,6 +93,10 @@ class FakeEndpointChannel : public EndpointChannel {
}
void SetAnalyticsRecorder(analytics::AnalyticsRecorder* analytics_recorder,
const std::string& endpoint_id) override {}
void SetLocalEndpointId(const std::string& local_endpoint_id) override {
local_endpoint_id_ = local_endpoint_id;
}
std::string GetLocalEndpointId() const override { return local_endpoint_id_; }
void set_read_output(ExceptionOr<ByteArray> output) { read_output_ = output; }
void set_write_output(Exception output) { write_output_ = output; }
@@ -113,6 +117,7 @@ class FakeEndpointChannel : public EndpointChannel {
bool is_paused_ = false;
location::nearby::proto::connections::DisconnectionReason
disconnection_reason_;
std::string local_endpoint_id_;
mutable uint32_t next_keep_alive_seq_no_ = 0;
};
@@ -83,6 +83,11 @@ constexpr auto kEnableWifiDirectGcOnly =
// by default, enable Wi-Fi Hotspot client.
constexpr auto kEnableWifiHotspotClient =
flags::Flag<bool>(kConfigPackage, "45648734", true);
// Enforces frame filtering on unconfirmed endpoints in EndpointManager and
// BaseEndpointChannel so application payloads and upgrade requests are blocked
// before connection acceptance.
constexpr auto kFilterUnconfirmedEndpointFrames =
flags::Flag<bool>(kConfigPackage, "45813128", true);
// When true, fix the BleServerSocket deadlock/use-after-free (b/494335036).
constexpr auto kFixBleServerSocketDeadlock =
flags::Flag<bool>(kConfigPackage, "45782647", true);
+1
View File
@@ -248,6 +248,7 @@ cc_test(
"//connections/implementation:client_proxy",
"//connections/implementation:endpoint_channel",
"//connections/implementation:offline_frames",
"//connections/implementation:service_id_constants",
"//connections/implementation/flags:connections_flags",
"//internal/flags:nearby_flags",
"//internal/platform:base",
@@ -29,7 +29,6 @@ cc_library(
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:util",
"@com_google_absl//absl/strings",
],
)
@@ -45,7 +44,6 @@ cc_library(
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform:util",
"@com_google_absl//absl/strings",
],
)
@@ -54,9 +52,7 @@ cc_library(
srcs = ["advertisement_util.cc"],
hdrs = ["advertisement_util.h"],
deps = [
":dct_advertisement",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:util",
"@com_google_absl//absl/strings:string_view",
],
@@ -79,8 +75,6 @@ cc_test(
srcs = ["dct_advertisement_test.cc"],
deps = [
":dct_advertisement",
"//internal/platform:base",
"//internal/platform:util",
"//internal/platform/implementation/g3",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
@@ -93,7 +87,6 @@ cc_test(
deps = [
":util",
"//internal/platform:base",
"//internal/platform:util",
"//internal/platform/implementation/g3",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
@@ -129,7 +129,8 @@ std::optional<DctAdvertisement> DctAdvertisement::Parse(
LOG(WARNING) << "Failed to read device information.";
return std::nullopt;
}
if (device_information->type() != kDataTypeDeviceInformation) {
if (device_information->type() != kDataTypeDeviceInformation ||
device_information->value().empty()) {
LOG(WARNING) << "Invalid device information.";
return std::nullopt;
}
@@ -113,5 +113,10 @@ TEST(DctAdvertisementTest, ParseData) {
EXPECT_EQ(dct_advertisement->GetPsm(), 192);
}
TEST(DctAdvertisementTest, ParseWithEmptyDeviceInformationDataElement) {
std::string data = std::string("\x20\x25\0\0\x24\0\0\x07", 8);
EXPECT_FALSE(DctAdvertisement::Parse(data).has_value());
}
} // namespace
} // namespace nearby::connections::advertisements::ble
@@ -182,6 +182,7 @@ AwdlBwuHandler::CreateUpgradedEndpointChannel(
OperationResultCode::NEARBY_AWDL_ENDPOINT_CHANNEL_CREATION_FAILURE)};
}
awdl_medium_.StopDiscovery(upgrade_service_id);
return {std::move(channel)};
}
@@ -43,7 +43,8 @@
#include "internal/platform/mock_input_stream.h"
#include "internal/platform/mock_output_stream.h"
#include "internal/platform/nsd_service_info.h"
#include "internal/platform/output_stream.h"
#include "connections/implementation/service_id_constants.h"
namespace nearby {
@@ -175,7 +176,9 @@ TEST_F(AwdlBwuHandlerTest, CreateUpgradedEndpointChannel_Success) {
}
return true;
});
EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_))
.Times(1)
.WillRepeatedly(Return(true));
EXPECT_CALL(*awdl_medium_mock, ConnectToService(_, _, _))
.WillOnce(Return(ByMove(std::move(awdl_socket))));
@@ -191,6 +194,8 @@ TEST_F(AwdlBwuHandlerTest, CreateUpgradedEndpointChannel_Success) {
path_info);
EXPECT_TRUE(result.has_value());
EXPECT_FALSE(mediums_.GetAwdl().IsDiscovering(
WrapInitiatorUpgradeServiceId(kServiceId)));
}
TEST_F(AwdlBwuHandlerTest,
@@ -66,8 +66,7 @@ constexpr absl::Duration kAdvertisementHeaderExpiry = absl::Seconds(15);
// Private c'tor for testing.
DiscoveredPeripheralTracker::DiscoveredPeripheralTracker(
bool is_extended_advertisement_available, bool start_fetch_executor)
: is_extended_advertisement_available_(
is_extended_advertisement_available),
: is_extended_advertisement_available_(is_extended_advertisement_available),
start_fetch_executor_(start_fetch_executor) {}
DiscoveredPeripheralTracker::DiscoveredPeripheralTracker(
@@ -75,9 +74,7 @@ DiscoveredPeripheralTracker::DiscoveredPeripheralTracker(
: DiscoveredPeripheralTracker(is_extended_advertisement_available,
/*start_fetch_executor=*/true) {}
DiscoveredPeripheralTracker::~DiscoveredPeripheralTracker() {
Shutdown();
}
DiscoveredPeripheralTracker::~DiscoveredPeripheralTracker() { Shutdown(); }
void DiscoveredPeripheralTracker::StartFetchExecutorIfNeeded() {
if (executor_ != nullptr) {
@@ -199,7 +196,8 @@ void DiscoveredPeripheralTracker::ProcessFoundBleAdvertisement(
return;
}
if (advertisement_data.service_data.contains(bleutils::kDctServiceUuid)) {
if (!dct_service_id_hash_to_service_id_map_.empty() &&
advertisement_data.service_data.contains(bleutils::kDctServiceUuid)) {
std::optional<BleAdvertisementData> dct_advertisement_data =
HandleDctAdvertisement(advertisement_data);
@@ -749,15 +747,14 @@ void DiscoveredPeripheralTracker::HandleAdvertisementHeader(
// support extended advertisement.
if (!advertisement_header.IsSupportExtendedAdvertisement()) {
for (auto& item : service_id_infos_) {
item.second.discovered_peripheral_callback
.legacy_device_discovered_cb();
item.second.discovered_peripheral_callback.legacy_device_discovered_cb();
}
}
// Determine whether or not we need to read a fresh GATT advertisement.
VLOG(1) << "Handle GATT advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView())
advertisement_header.GetAdvertisementHash().AsStringView())
<< " in thread";
if (!ShouldReadRawAdvertisementFromServer(advertisement_header)) {
@@ -1015,7 +1012,7 @@ void DiscoveredPeripheralTracker::GattFetchingLoop() {
}
if (!found_task) {
LOG(WARNING) << "No task found, skip to fetch raw advertisement.";
continue;;
continue;
}
// Check if the task is expired.
@@ -1027,8 +1024,7 @@ void DiscoveredPeripheralTracker::GattFetchingLoop() {
.AsStringView())
<< " is expired, skip to fetch raw advertisement.";
} else {
FetchRawAdvertisementsInThread(task.peripheral,
task.advertisement_header,
FetchRawAdvertisementsInThread(task.peripheral, task.advertisement_header,
std::move(task.advertisement_fetcher));
}
// Clear in progress header after the task is done.
@@ -60,7 +60,8 @@ class WifiDirect {
bool IsGOStarted() ABSL_LOCKS_EXCLUDED(mutex_);
// Start WifiDirect Group Owner. Returns true if WifiDirect GO is successfully
// started.
bool StartWifiDirect() ABSL_LOCKS_EXCLUDED(mutex_);
bool StartWifiDirect()
ABSL_LOCKS_EXCLUDED(mutex_);
// Stop WifiDirect Group Owner
bool StopWifiDirect() ABSL_LOCKS_EXCLUDED(mutex_);
@@ -54,6 +54,11 @@ WifiDirectBwuHandler::WifiDirectBwuHandler(
std::string WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint(
ClientProxy* client, const std::string& upgrade_service_id,
const std::string& endpoint_id) {
auto remote_device_name = client->GetRemoteDeviceName(endpoint_id);
WifiDirectCredentials* wifi_direct_crendential =
wifi_direct_medium_.GetCredentials(upgrade_service_id);
wifi_direct_crendential->SetRemoteDeviceName(remote_device_name);
// Create WifiDirect GO
if (!wifi_direct_medium_.StartWifiDirect()) {
LOG(INFO) << "Failed to start Wifi Direct!";
@@ -82,7 +87,7 @@ std::string WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint(
// Note: Credentials are not generated until Medium StartWifiDirect() is
// called and the server socket is created. Be careful moving this codeblock
// around.
WifiDirectCredentials* wifi_direct_crendential =
wifi_direct_crendential =
wifi_direct_medium_.GetCredentials(upgrade_service_id);
std::string ssid = wifi_direct_crendential->GetSSID();
std::string password = wifi_direct_crendential->GetPassword();
@@ -194,7 +194,9 @@ WifiHotspotBwuHandler::CreateUpgradedEndpointChannel(
}
// Add gateway and port to address candidates if address candidates is empty.
if (service_addresses.empty() &&
upgrade_path_info_credentials.has_gateway()) {
upgrade_path_info_credentials.has_gateway() &&
upgrade_path_info_credentials.port() > 0 &&
upgrade_path_info_credentials.port() <= 65535) {
std::vector<char> address_bytes =
GatewayToAddressBytes(upgrade_path_info_credentials.gateway());
if (!address_bytes.empty()) {
@@ -230,7 +232,8 @@ WifiHotspotBwuHandler::CreateUpgradedEndpointChannel(
LOG(ERROR) << "WifiHotspotBwuHandler failed to connect to the WifiHotspot "
"service for endpoint "
<< endpoint_id;
return {Error(socket_result.error().operation_result_code().value())};
return {Error(socket_result.error().operation_result_code().value_or(
OperationResultCode::DETAIL_UNKNOWN))};
}
VLOG(1)
<< "WifiHotspotBwuHandler successfully connected to WifiHotspot service "
@@ -55,8 +55,9 @@ class WifiHotspotTest : public testing::Test {
~WifiHotspotTest() override { env_.Stop(); }
void SetUp() override {
nearby::NearbyFlags::GetInstance().OverrideInt64FlagValue(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotConnectionIntervalMillis, 1);
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotConnectionIntervalMillis,
1);
}
void TearDown() override {
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
@@ -89,9 +90,10 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) {
ExceptionOr<OfflineFrame> upgrade_frame;
auto handler_1 = std::make_unique<WifiHotspotBwuHandler>(
&mediums_HS_ap.GetWifiHotspot(), [&](ClientProxy* client,
std::unique_ptr<BwuHandler::IncomingSocketConnection>
mutable_connection) {
&mediums_HS_ap.GetWifiHotspot(),
[&](ClientProxy* client,
std::unique_ptr<BwuHandler::IncomingSocketConnection>
mutable_connection) {
LOG(INFO) << "Server socket connection accept call back, Socket name: "
<< mutable_connection->socket->ToString();
accept_latch.CountDown();
@@ -169,5 +171,42 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) {
EXPECT_FALSE(mediums_HS_sta.GetWifiHotspot().IsConnectedToHotspot());
}
TEST_F(WifiHotspotTest, CreateUpgradedEndpointChannel_RejectGatewayPort0) {
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointID));
Mediums mediums;
WifiHotspotBwuHandler handler(&mediums.GetWifiHotspot(), nullptr);
UpgradePathInfo path_info;
auto* credentials = path_info.mutable_wifi_hotspot_credentials();
credentials->set_ssid("SSID");
credentials->set_password("password");
credentials->set_gateway("192.168.43.1");
// Port 0
credentials->set_port(0);
auto result = handler.CreateUpgradedEndpointChannel(
&client, std::string(kServiceID), std::string(kEndpointID), path_info);
EXPECT_TRUE(result.has_error());
EXPECT_EQ(result.error().operation_result_code().value(),
OperationResultCode::CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL);
// Port > 65535
credentials->set_port(65536);
result = handler.CreateUpgradedEndpointChannel(
&client, std::string(kServiceID), std::string(kEndpointID), path_info);
EXPECT_TRUE(result.has_error());
EXPECT_EQ(result.error().operation_result_code().value(),
OperationResultCode::CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL);
// Port < 0
credentials->set_port(-1);
result = handler.CreateUpgradedEndpointChannel(
&client, std::string(kServiceID), std::string(kEndpointID), path_info);
EXPECT_TRUE(result.has_error());
EXPECT_EQ(result.error().operation_result_code().value(),
OperationResultCode::CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL);
}
} // namespace connections
} // namespace nearby
@@ -79,21 +79,42 @@ WifiLanBwuHandler::CreateUpgradedEndpointChannel(
<< address_candidate.ip_address().size();
continue;
}
if (service_address.IsLoopbackAddress() ||
service_address.IsLinkLocalAddress()) {
LOG(WARNING) << "Loopback/link-local address candidate is rejected.";
return {
Error(OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL)};
}
address_candidates.push_back(std::move(service_address));
}
// Only use ip_address and wifi_port if address_candidates is empty.
if (address_candidates.empty()) {
address_candidates.push_back(ServiceAddress{
.address =
std::vector<char>(upgrade_path_info_socket.ip_address().begin(),
upgrade_path_info_socket.ip_address().end()),
.port = static_cast<uint16_t>(upgrade_path_info_socket.wifi_port())});
if (upgrade_path_info_socket.ip_address().size() != 4 ||
upgrade_path_info_socket.wifi_port() <= 0 ||
upgrade_path_info_socket.wifi_port() > 65535) {
LOG(ERROR) << "WifiLanBwuHandler: fallback ip_address size is not 4 "
<< "or port is invalid (IPv4 only).";
return {
Error(OperationResultCode::CONNECTIVITY_WIFI_LAN_IP_ADDRESS_ERROR)};
}
ServiceAddress service_address;
service_address.address = {upgrade_path_info_socket.ip_address().begin(),
upgrade_path_info_socket.ip_address().end()};
service_address.port =
static_cast<uint16_t>(upgrade_path_info_socket.wifi_port());
if (service_address.IsLoopbackAddress() ||
service_address.IsLinkLocalAddress()) {
LOG(WARNING) << "Loopback/link-local fallback address is rejected.";
return {
Error(OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL)};
}
address_candidates.push_back(std::move(service_address));
}
Error error;
for (const auto& address_candidate : address_candidates) {
VLOG(1) << "WifiLanBwuHandler is attempting to connect to available "
"WifiLan service (" << address_candidate << ") for endpoint "
<< endpoint_id;
"WifiLan service ("
<< address_candidate << ") for endpoint " << endpoint_id;
std::shared_ptr<CancellationFlag> cancellation_flag =
client->GetCancellationFlag(endpoint_id);
ErrorOr<WifiLanSocket> socket_result = wifi_lan_medium_.Connect(
@@ -102,7 +123,8 @@ WifiLanBwuHandler::CreateUpgradedEndpointChannel(
LOG(ERROR)
<< "WifiLanBwuHandler failed to connect to the WifiLan service ("
<< address_candidate << ") for endpoint " << endpoint_id;
error = Error(socket_result.error().operation_result_code().value());
error = Error(socket_result.error().operation_result_code().value_or(
OperationResultCode::DETAIL_UNKNOWN));
continue;
}
VLOG(1) << "WifiLanBwuHandler successfully connected to WifiLan service ("
@@ -209,6 +209,98 @@ TEST_F(WifiLanBwuHandlerTest,
EXPECT_TRUE(result.has_value());
};
TEST_F(WifiLanBwuHandlerTest,
CreateUpgradedEndpointChannel_RejectLoopbackAndLinkLocalCandidates) {
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointId));
BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info;
// 1st candidate: Loopback
auto* address_candidate =
path_info.mutable_wifi_lan_socket()->add_address_candidates();
address_candidate->set_ip_address(std::string("\x7f\x00\x00\x01", 4));
address_candidate->set_port(8080);
// 2nd candidate: Link-Local (169.254.1.1)
address_candidate =
path_info.mutable_wifi_lan_socket()->add_address_candidates();
address_candidate->set_ip_address("\xa9\xfe\x01\x01");
address_candidate->set_port(8080);
// 3rd candidate: Valid IP
address_candidate =
path_info.mutable_wifi_lan_socket()->add_address_candidates();
address_candidate->set_ip_address(kIpv4Address);
address_candidate->set_port(8080);
auto result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId),
std::move(path_info));
EXPECT_FALSE(result.has_value());
}
TEST_F(WifiLanBwuHandlerTest,
CreateUpgradedEndpointChannel_RejectLoopbackFallbackIp) {
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointId));
BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info;
path_info.mutable_wifi_lan_socket()->set_ip_address(
std::string("\x7f\x00\x00\x01", 4));
path_info.mutable_wifi_lan_socket()->set_wifi_port(8080);
auto result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId),
std::move(path_info));
EXPECT_FALSE(result.has_value());
}
TEST_F(WifiLanBwuHandlerTest,
CreateUpgradedEndpointChannel_RejectInvalidFallbackIpLength) {
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointId));
BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info;
path_info.mutable_wifi_lan_socket()->set_ip_address("123");
path_info.mutable_wifi_lan_socket()->set_wifi_port(8080);
auto result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId),
std::move(path_info));
EXPECT_FALSE(result.has_value());
}
TEST_F(WifiLanBwuHandlerTest,
CreateUpgradedEndpointChannel_RejectFallbackPort0) {
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointId));
BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info;
path_info.mutable_wifi_lan_socket()->set_ip_address(kIpv4Address);
// Port 0
path_info.mutable_wifi_lan_socket()->set_wifi_port(0);
auto result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId), path_info);
EXPECT_FALSE(result.has_value());
// Port > 65535
path_info.mutable_wifi_lan_socket()->set_wifi_port(65536);
result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId), path_info);
EXPECT_FALSE(result.has_value());
// Port < 0
path_info.mutable_wifi_lan_socket()->set_wifi_port(-1);
result = handler_.CreateUpgradedEndpointChannel(
&client, std::string(kServiceId), std::string(kEndpointId), path_info);
EXPECT_FALSE(result.has_value());
}
TEST_F(WifiLanBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ClientProxy client;
@@ -289,9 +381,8 @@ TEST_F(WifiLanBwuHandlerTest,
mediums_.GetWifiLan().IsAcceptingConnections("service_id_UPGRADE"));
}
TEST_F(
WifiLanBwuHandlerTest,
InitializeUpgradedMediumForEndpoint_AlreadyAccepting_KeepAccepting) {
TEST_F(WifiLanBwuHandlerTest,
InitializeUpgradedMediumForEndpoint_AlreadyAccepting_KeepAccepting) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ClientProxy client;
client.AddCancellationFlag(std::string(kEndpointId));
@@ -69,6 +69,9 @@ class MockEndpointChannel : public EndpointChannel {
MOCK_METHOD(uint32_t, GetNextKeepAliveSeqNo, (), (const, override));
MOCK_METHOD(void, SetAnalyticsRecorder,
(analytics::AnalyticsRecorder*, const std::string&), (override));
MOCK_METHOD(void, SetLocalEndpointId, (const std::string& local_endpoint_id),
(override));
MOCK_METHOD(std::string, GetLocalEndpointId, (), (const, override));
};
} // namespace nearby::connections
+16 -7
View File
@@ -180,7 +180,8 @@ std::string ForConnectionRequestPresence(
return frame.SerializeAsString();
}
std::string ForConnectionResponse(std::int32_t status, const OsInfo& os_info) {
std::string ForConnectionResponse(std::int32_t status, const OsInfo& os_info,
const std::string& device_name) {
OfflineFrame frame;
frame.set_version(OfflineFrame::V1);
@@ -201,6 +202,7 @@ std::string ForConnectionResponse(std::int32_t status, const OsInfo& os_info) {
NearbyFlags::GetInstance().GetInt64Flag(
config_package_nearby::nearby_connections_feature::
kSafeToDisconnectVersion));
sub_frame->set_wifi_direct_device_name(device_name);
return frame.SerializeAsString();
}
@@ -460,6 +462,7 @@ std::string ForBwuSafeToClose() {
}
std::string ForBwuIntroduction(const std::string& endpoint_id,
const std::string& last_endpoint_id,
bool supports_disabling_encryption) {
OfflineFrame frame;
@@ -473,6 +476,9 @@ std::string ForBwuIntroduction(const std::string& endpoint_id,
client_introduction->set_endpoint_id(endpoint_id);
client_introduction->set_supports_disabling_encryption(
supports_disabling_encryption);
if (!last_endpoint_id.empty()) {
client_introduction->set_last_endpoint_id(last_endpoint_id);
}
return frame.SerializeAsString();
}
@@ -498,16 +504,14 @@ std::string ForBwuFailure(const UpgradePathInfo& info) {
v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION);
auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation();
sub_frame->set_event_type(BandwidthUpgradeNegotiationFrame::UPGRADE_FAILURE);
auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info();
*upgrade_path_info = info;
*sub_frame->mutable_upgrade_path_info() = info;
return frame.SerializeAsString();
}
std::string ForBwuPathRequest(const std::vector<Medium>& mediums,
const MediumRole& medium_role) {
std::string ForBwuPathRequest(Medium medium, const std::vector<Medium>& mediums,
const MediumRole& medium_role,
bool supports_5_ghz) {
OfflineFrame frame;
frame.set_version(OfflineFrame::V1);
@@ -516,11 +520,16 @@ std::string ForBwuPathRequest(const std::vector<Medium>& mediums,
auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation();
sub_frame->set_event_type(
BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_REQUEST);
auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info();
upgrade_path_info->set_medium(MediumToUpgradePathInfoMedium(medium));
auto* upgrade_path_request =
sub_frame->mutable_upgrade_path_info()->mutable_upgrade_path_request();
upgrade_path_info->mutable_upgrade_path_request();
for (const auto& medium : mediums) {
upgrade_path_request->add_mediums(MediumToUpgradePathInfoMedium(medium));
}
LOG(INFO) << "ForBwuPathRequest: supports_5_ghz: " << supports_5_ghz;
upgrade_path_request->mutable_medium_meta_data()->set_supports_5_ghz(
supports_5_ghz);
auto* role =
upgrade_path_request->mutable_medium_meta_data()->mutable_medium_role();
role->MergeFrom(medium_role);
+6 -3
View File
@@ -59,7 +59,8 @@ std::string ForConnectionRequestPresence(
const location::nearby::connections::PresenceDevice& proto_presence_device,
const ConnectionInfo& connection_info);
std::string ForConnectionResponse(
std::int32_t status, const location::nearby::connections::OsInfo& os_info);
std::int32_t status, const location::nearby::connections::OsInfo& os_info,
const std::string& device_name);
// Builds Payload transfer messages.
std::string ForDataPayloadTransfer(
@@ -76,6 +77,7 @@ std::string ForPayloadAckPayloadTransfer(std::int64_t payload_id);
// Builds Bandwidth Upgrade [BWU] messages.
std::string ForBwuIntroduction(const std::string& endpoint_id,
const std::string& last_endpoint_id,
bool supports_disabling_encryption);
std::string ForBwuIntroductionAck();
std::string ForBwuWifiHotspotPathAvailable(
@@ -107,8 +109,9 @@ std::string ForBwuWebrtcPathAvailable(
const location::nearby::connections::LocationHint& location_hint_a);
std::string ForBwuFailure(const UpgradePathInfo& info);
std::string ForBwuPathRequest(
const std::vector<Medium>& mediums,
const location::nearby::connections::MediumRole& medium_role);
Medium medium, const std::vector<Medium>& mediums,
const location::nearby::connections::MediumRole& medium_role,
bool supports_5_ghz);
std::string ForBwuLastWrite();
std::string ForBwuSafeToClose();
@@ -352,6 +352,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionResponse) {
os_info { type: LINUX }
multiplex_socket_bitmask: 0
safe_to_disconnect_version: 5
wifi_direct_device_name: "device_name"
>
>)pb";
@@ -361,7 +362,8 @@ TEST(OfflineFramesTest, CanGenerateConnectionResponse) {
config_package_nearby::nearby_connections_feature::
kSafeToDisconnectVersion,
5);
auto response = FromBytes(ForConnectionResponse(1, os_info));
auto response = FromBytes(
ForConnectionResponse(1, os_info, "device_name"));
ASSERT_TRUE(response.ok());
OfflineFrame message = response.result();
EXPECT_THAT(message, EqualsProto(kExpected));
@@ -502,10 +504,7 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiLanPathAvailable) {
ip_address: "\x2a\x00\x79\xe0\x2e\x87\x00\x06\xb7\x28\x67\x45\x7a\xdd\x01\x53"
port: 1234
>
address_candidates: <
ip_address: "\001\002\003\004"
port: 1234
>
address_candidates: < ip_address: "\001\002\003\004" port: 1234 >
>
supports_client_introduction_ack: true
>
@@ -679,11 +678,13 @@ TEST(OfflineFramesTest, CanGenerateBwuIntroduction) {
client_introduction: <
endpoint_id: "ABC"
supports_disabling_encryption: false
last_endpoint_id: "DEF"
>
>
>)pb";
auto response = FromBytes(ForBwuIntroduction(
std::string(kEndpointId), false /* supports_disabling_encryption */));
auto response =
FromBytes(ForBwuIntroduction(std::string(kEndpointId), "DEF",
false /* supports_disabling_encryption */));
ASSERT_TRUE(response.ok());
OfflineFrame message = response.result();
EXPECT_THAT(message, EqualsProto(kExpected));
@@ -722,7 +723,6 @@ TEST(OfflineFramesTest, CanGenerateDisconnection) {
EXPECT_THAT(message, EqualsProto(kExpected));
}
TEST(OfflineFramesTest, CanGenerateBwuPathRequest) {
constexpr absl::string_view kExpected =
R"pb(
@@ -732,9 +732,11 @@ TEST(OfflineFramesTest, CanGenerateBwuPathRequest) {
bandwidth_upgrade_negotiation: <
event_type: UPGRADE_PATH_REQUEST
upgrade_path_info: <
medium: WIFI_HOTSPOT
upgrade_path_request: <
mediums: WIFI_HOTSPOT
medium_meta_data: <
supports_5_ghz: true
medium_role: < support_wifi_hotspot_client: true >
>
>
@@ -745,7 +747,9 @@ TEST(OfflineFramesTest, CanGenerateBwuPathRequest) {
mediums.push_back(Medium::WIFI_HOTSPOT);
MediumRole medium_role;
medium_role.set_support_wifi_hotspot_client(true);
auto response = FromBytes(ForBwuPathRequest(mediums, medium_role));
auto response =
FromBytes(ForBwuPathRequest(Medium::WIFI_HOTSPOT, mediums, medium_role,
/*supports_5_ghz=*/true));
ASSERT_TRUE(response.ok());
OfflineFrame message = response.result();
EXPECT_THAT(message, EqualsProto(kExpected));
@@ -19,6 +19,8 @@
#include <regex> //NOLINT
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "connections/implementation/internal_payload.h"
#include "connections/implementation/offline_frames.h"
@@ -26,9 +28,12 @@
#include "connections/medium_selector.h"
#include "internal/platform/exception.h"
#include "internal/platform/logging.h"
#include "internal/platform/service_address.h"
#include "sharing/internal/base/utf_string_conversions.h"
namespace nearby {
namespace connections {
namespace parser {
namespace {
@@ -72,7 +77,12 @@ constexpr int kWifiDirectPinMinLength = 0;
constexpr int kWifiDirectPinMaxLength = 16;
inline bool WithinRange(int value, int min, int max) {
return value >= min && value < max;
return value >= min && value <= max;
}
bool IsValidWifiLanServiceAddress(const ServiceAddress& service_address) {
return !service_address.IsLoopbackAddress() &&
!service_address.IsLinkLocalAddress();
}
Exception EnsureValidConnectionRequestFrame(
@@ -140,16 +150,25 @@ Exception EnsureValidPayloadTransferControlFrame(
return {Exception::kSuccess};
}
bool CheckForIllegalCharacters(std::string toBeValidated,
bool CheckForIllegalCharacters(absl::string_view toBeValidated,
const absl::string_view illegalPatterns[],
size_t illegalPatternsSize) {
if (toBeValidated.empty()) {
return false;
}
// Null bytes are rejected to prevent null-byte injection attacks. C-style
// APIs (like system file operations) treat '\0' as a string terminator,
// whereas C++ strings can contain them. This discrepancy can lead to
// validation bypasses (e.g., validating "file.sh\0.png" as a PNG but
// creating "file.sh" on disk).
if (absl::StrContains(toBeValidated, '\0') ||
!nearby::utils::IsStringUtf8(toBeValidated)) {
return true;
}
for (int index = 0; index < illegalPatternsSize; index++) {
if (toBeValidated.find(std::string(illegalPatterns[index])) !=
std::string::npos) {
if (absl::StrContains(toBeValidated, illegalPatterns[index])) {
return true;
}
}
@@ -178,20 +197,21 @@ Exception EnsureValidPayloadTransferFrame(const PayloadTransferFrame& frame) {
location::nearby::connections::PayloadTransferFrame::PayloadHeader::
FILE) {
if (frame.payload_header().has_file_name()) {
if (CheckForIllegalCharacters(frame.payload_header().file_name(),
kIllegalFileNamePatterns,
const std::string& file_name = frame.payload_header().file_name();
if (CheckForIllegalCharacters(file_name, kIllegalFileNamePatterns,
kIllegalFileNamePatternsSize)) {
LOG(ERROR) << "File name " << frame.payload_header().file_name()
<< " has illegal characters";
LOG(ERROR) << "File name (hex) " << absl::BytesToHexString(file_name)
<< " has illegal characters or invalid UTF-8";
return {Exception::kIllegalCharacters};
}
}
if (frame.payload_header().has_parent_folder()) {
if (CheckForIllegalCharacters(frame.payload_header().parent_folder(),
kIllegalParentFolderPatterns,
const std::string& parent_folder = frame.payload_header().parent_folder();
if (CheckForIllegalCharacters(parent_folder, kIllegalParentFolderPatterns,
kIllegalParentFolderPatternsSize)) {
LOG(ERROR) << "Parent folder " << frame.payload_header().parent_folder()
<< " has illegal characters";
LOG(ERROR) << "Parent folder (hex) "
<< absl::BytesToHexString(parent_folder)
<< " has illegal characters or invalid UTF-8";
return {Exception::kIllegalCharacters};
}
}
@@ -234,21 +254,29 @@ Exception EnsureValidBandwidthUpgradeWifiHotspotPathAvailableFrame(
!WithinRange(wifi_hotspot_credentials.password().length(),
kWifiPasswordSsidMinLength, kWifiPasswordSsidMaxLength))
return {Exception::kInvalidProtocolBuffer};
if (!wifi_hotspot_credentials.has_gateway() &&
wifi_hotspot_credentials.address_candidates_size() == 0)
if ((!wifi_hotspot_credentials.has_gateway() ||
wifi_hotspot_credentials.gateway().empty()) &&
wifi_hotspot_credentials.address_candidates_size() == 0) {
return {Exception::kInvalidProtocolBuffer};
const std::regex ip4_pattern(std::string(kIpv4PatternString).c_str());
if (!wifi_hotspot_credentials.gateway().empty() &&
!(std::regex_match(wifi_hotspot_credentials.gateway(), ip4_pattern))) {
return {Exception::kInvalidProtocolBuffer};
}
for (const auto& address_candidate :
wifi_hotspot_credentials.address_candidates()) {
if (!address_candidate.has_ip_address() || !address_candidate.has_port()) {
const std::regex ip4_pattern(std::string(kIpv4PatternString).c_str());
if (wifi_hotspot_credentials.has_gateway() &&
!wifi_hotspot_credentials.gateway().empty()) {
if (!(std::regex_match(wifi_hotspot_credentials.gateway(), ip4_pattern))) {
return {Exception::kInvalidProtocolBuffer};
}
if (address_candidate.ip_address().size() != 4 &&
address_candidate.ip_address().size() != 16) {
if (!wifi_hotspot_credentials.has_port() ||
!WithinRange(wifi_hotspot_credentials.port(), 1, 65535)) {
return {Exception::kInvalidProtocolBuffer};
}
}
for (const auto& address_candidate :
wifi_hotspot_credentials.address_candidates()) {
ServiceAddress service_address;
if (!ServiceAddressFromProto(address_candidate, service_address)) {
return {Exception::kInvalidProtocolBuffer};
}
}
@@ -265,6 +293,29 @@ Exception EnsureValidBandwidthUpgradeWifiLanPathAvailableFrame(
return {Exception::kInvalidProtocolBuffer};
}
if (wifi_lan_socket.has_ip_address()) {
location::nearby::connections::ServiceAddress proto;
proto.set_ip_address(wifi_lan_socket.ip_address());
proto.set_port(wifi_lan_socket.wifi_port());
ServiceAddress service_address;
if (!ServiceAddressFromProto(proto, service_address)) {
return {Exception::kInvalidProtocolBuffer};
}
if (!IsValidWifiLanServiceAddress(service_address)) {
return {Exception::kInvalidProtocolBuffer};
}
}
for (const auto& address_candidate : wifi_lan_socket.address_candidates()) {
ServiceAddress service_address;
if (!ServiceAddressFromProto(address_candidate, service_address)) {
return {Exception::kInvalidProtocolBuffer};
}
if (!IsValidWifiLanServiceAddress(service_address)) {
return {Exception::kInvalidProtocolBuffer};
}
}
// For backwards compatibility reasons, no other fields should be null-checked
// for this frame. Parameter checking (eg. must be within this range) is fine.
return {Exception::kSuccess};
@@ -292,7 +343,7 @@ Exception EnsureValidBandwidthUpgradeWifiDirectPathAvailableFrame(
std::string(kWifiDirectSsidPatternString).c_str());
bool ssid_valid =
wifi_direct_credentials.has_ssid() &&
wifi_direct_credentials.ssid().length() < kWifiDirectSsidMaxLength &&
wifi_direct_credentials.ssid().length() <= kWifiDirectSsidMaxLength &&
std::regex_match(wifi_direct_credentials.ssid(), ssid_pattern);
bool password_valid =
wifi_direct_credentials.has_password() &&
@@ -300,8 +351,7 @@ Exception EnsureValidBandwidthUpgradeWifiDirectPathAvailableFrame(
kWifiPasswordSsidMinLength, kWifiPasswordSsidMaxLength);
bool device_name_valid =
wifi_direct_credentials.has_device_name() &&
wifi_direct_credentials.device_name().length() <
kWifiDirectSsidMaxLength;
wifi_direct_credentials.device_name().length() < kWifiDirectSsidMaxLength;
bool pin_valid =
wifi_direct_credentials.has_pin() &&
WithinRange(wifi_direct_credentials.pin().length(),
@@ -36,6 +36,7 @@ using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame;
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::PayloadTransferFrame;
using ::location::nearby::connections::V1Frame;
constexpr absl::string_view kEndpointId{"ABC"};
constexpr absl::string_view kEndpointName{"XYZ"};
@@ -122,7 +123,7 @@ TEST_F(OfflineFramesConnectionRequestTest,
ValidatesAsFailWithEmptyEndpointIdInConnectionRequestFrame) {
connection_info_.local_endpoint_id = "";
std::string bytes = ForConnectionRequestConnections({}, connection_info_);
location::nearby::connections::OfflineFrame frame;
OfflineFrame frame;
frame.ParseFromString(bytes);
frame.mutable_v1()->mutable_connection_request()->set_endpoint_id("");
ASSERT_TRUE(frame.v1().connection_request().has_endpoint_id());
@@ -179,7 +180,8 @@ TEST(OfflineFramesValidatorTest,
OfflineFrame offline_frame;
OsInfo os_info;
std::string bytes = ForConnectionResponse(kStatusAccepted, os_info);
std::string bytes =
ForConnectionResponse(kStatusAccepted, os_info, "device_name");
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
@@ -192,7 +194,8 @@ TEST(OfflineFramesValidatorTest,
OfflineFrame offline_frame;
OsInfo os_info;
std::string bytes = ForConnectionResponse(kStatusAccepted, os_info);
std::string bytes =
ForConnectionResponse(kStatusAccepted, os_info, "device_name");
offline_frame.ParseFromString(bytes);
auto* v1_frame = offline_frame.mutable_v1();
@@ -208,7 +211,7 @@ TEST(OfflineFramesValidatorTest,
OfflineFrame offline_frame;
OsInfo os_info;
std::string bytes = ForConnectionResponse(-1, os_info);
std::string bytes = ForConnectionResponse(-1, os_info, "device_name");
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
@@ -365,6 +368,92 @@ TEST(OfflineFramesValidatorTest,
EXPECT_EQ(ret_value.value, Exception::kIllegalCharacters);
}
TEST(OfflineFramesValidatorTest, ValidatesAsFailedTypeFileWithNonUtf8FilePath) {
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::PayloadChunk chunk;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::FILE);
header.set_total_size(100);
header.set_file_name(std::string("hello\xffworld"));
header.set_parent_folder(std::string());
chunk.set_body("payload data");
chunk.set_offset(0);
chunk.set_flags(1);
OfflineFrame offline_frame;
std::string bytes = ForDataPayloadTransfer(header, chunk);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_EQ(ret_value.value, Exception::kIllegalCharacters);
}
TEST(OfflineFramesValidatorTest,
ValidatesAsFailedTypeFileWithNonUtf8ParentFolder) {
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::PayloadChunk chunk;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::FILE);
header.set_total_size(100);
header.set_file_name(std::string("valid.txt"));
header.set_parent_folder(std::string("folder\xff"));
chunk.set_body("payload data");
chunk.set_offset(0);
chunk.set_flags(1);
OfflineFrame offline_frame;
std::string bytes = ForDataPayloadTransfer(header, chunk);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_EQ(ret_value.value, Exception::kIllegalCharacters);
}
TEST(OfflineFramesValidatorTest, ValidatesAsFailedTypeFileWithNullInFilePath) {
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::PayloadChunk chunk;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::FILE);
header.set_total_size(100);
header.set_file_name(std::string("hello\0world", 11));
header.set_parent_folder(std::string());
chunk.set_body("payload data");
chunk.set_offset(0);
chunk.set_flags(1);
OfflineFrame offline_frame;
std::string bytes = ForDataPayloadTransfer(header, chunk);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_EQ(ret_value.value, Exception::kIllegalCharacters);
}
TEST(OfflineFramesValidatorTest,
ValidatesAsFailedTypeFileWithNullInParentFolder) {
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::PayloadChunk chunk;
header.set_id(12345);
header.set_type(PayloadTransferFrame::PayloadHeader::FILE);
header.set_total_size(100);
header.set_file_name(std::string("valid.txt"));
header.set_parent_folder(std::string("folder\0name", 11));
chunk.set_body("payload data");
chunk.set_offset(0);
chunk.set_flags(1);
OfflineFrame offline_frame;
std::string bytes = ForDataPayloadTransfer(header, chunk);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_EQ(ret_value.value, Exception::kIllegalCharacters);
}
TEST(OfflineFramesValidatorTest, ValidatesAsFailWithNullPayloadTransferFrame) {
PayloadTransferFrame::PayloadHeader header;
PayloadTransferFrame::PayloadChunk chunk;
@@ -677,6 +766,86 @@ TEST(OfflineFramesValidatorTest,
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateHotspotUpgradeFrameWithLargePortCandidateFails) {
OfflineFrame offline_frame;
BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WifiHotspotCredentials
credentials;
credentials.set_ssid(kSsid);
credentials.set_password(kPassword);
credentials.set_frequency(kHotspotFrequency);
auto* candidate = credentials.mutable_address_candidates()->Add();
candidate->set_ip_address(std::string("\xc0\xa8\x00\x01", 4));
candidate->set_port(70000);
std::string bytes = ForBwuWifiHotspotPathAvailable(
std::move(credentials), kSupportsDisablingEncryption);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateHotspotUpgradeFrameWithFallbackInvalidPortFails) {
OfflineFrame offline_frame;
BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WifiHotspotCredentials
credentials;
credentials.set_ssid(kSsid);
credentials.set_password(kPassword);
credentials.set_frequency(kHotspotFrequency);
credentials.set_gateway(std::string(kWifiHotspotGateway));
credentials.set_port(70000);
std::string bytes = ForBwuWifiHotspotPathAvailable(
std::move(credentials), kSupportsDisablingEncryption);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateHotspotUpgradeFrameWithEmptyGatewayAndNoCandidatesFails) {
OfflineFrame offline_frame;
BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WifiHotspotCredentials
credentials;
credentials.set_ssid(kSsid);
credentials.set_password(kPassword);
credentials.set_frequency(kHotspotFrequency);
credentials.set_gateway("");
std::string bytes = ForBwuWifiHotspotPathAvailable(
std::move(credentials), kSupportsDisablingEncryption);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateHotspotUpgradeFrameWithNoGatewayAndNoCandidatesFails) {
OfflineFrame offline_frame;
BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WifiHotspotCredentials
credentials;
credentials.set_ssid(kSsid);
credentials.set_password(kPassword);
credentials.set_frequency(kHotspotFrequency);
// Do not set gateway
// Do not set address candidates
std::string bytes = ForBwuWifiHotspotPathAvailable(
std::move(credentials), kSupportsDisablingEncryption);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithAddressCandidatesSucceeds) {
OfflineFrame offline_frame;
@@ -694,6 +863,144 @@ TEST(OfflineFramesValidatorTest,
EXPECT_TRUE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithLoopbackAddressCandidateFails) {
OfflineFrame offline_frame;
std::vector<ServiceAddress> address_candidates = {
{{127, 0, 0, 1}, kPort},
};
std::string bytes = ForBwuWifiLanPathAvailable(address_candidates);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithLinkLocalAddressCandidateFails) {
OfflineFrame offline_frame;
std::vector<ServiceAddress> address_candidates = {
{{169, 254, 1, 1}, kPort},
};
std::string bytes = ForBwuWifiLanPathAvailable(address_candidates);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithInvalidIpAddressSizeCandidateFails) {
OfflineFrame offline_frame;
std::vector<ServiceAddress> address_candidates = {
{{1, 2, 3}, kPort},
};
std::string bytes = ForBwuWifiLanPathAvailable(address_candidates);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithZeroPortCandidateFails) {
OfflineFrame offline_frame;
std::vector<ServiceAddress> address_candidates = {
{{192, 168, 1, 1}, 0},
};
std::string bytes = ForBwuWifiLanPathAvailable(address_candidates);
offline_frame.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithLargePortCandidateFails) {
OfflineFrame offline_frame;
std::vector<ServiceAddress> address_candidates = {
{{192, 168, 1, 1}, kPort},
};
std::string bytes = ForBwuWifiLanPathAvailable(address_candidates);
offline_frame.ParseFromString(bytes);
auto* negotiation =
offline_frame.mutable_v1()->mutable_bandwidth_upgrade_negotiation();
auto* wifi_lan_socket =
negotiation->mutable_upgrade_path_info()->mutable_wifi_lan_socket();
if (wifi_lan_socket->address_candidates_size() > 0) {
wifi_lan_socket->mutable_address_candidates(0)->set_port(70000);
}
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithFallbackLoopbackAddressFails) {
OfflineFrame offline_frame;
offline_frame.set_version(OfflineFrame::V1);
auto* v1_frame = offline_frame.mutable_v1();
v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION);
auto* negotiation = v1_frame->mutable_bandwidth_upgrade_negotiation();
negotiation->set_event_type(
BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE);
auto* upgrade_path_info = negotiation->mutable_upgrade_path_info();
upgrade_path_info->set_medium(UpgradePathInfo::WIFI_LAN);
auto* wifi_lan_socket = upgrade_path_info->mutable_wifi_lan_socket();
wifi_lan_socket->set_ip_address(std::string({127, 0, 0, 1}));
wifi_lan_socket->set_wifi_port(kPort);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithFallbackLinkLocalAddressFails) {
OfflineFrame offline_frame;
offline_frame.set_version(OfflineFrame::V1);
auto* v1_frame = offline_frame.mutable_v1();
v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION);
auto* negotiation = v1_frame->mutable_bandwidth_upgrade_negotiation();
negotiation->set_event_type(
BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE);
auto* upgrade_path_info = negotiation->mutable_upgrade_path_info();
upgrade_path_info->set_medium(UpgradePathInfo::WIFI_LAN);
auto* wifi_lan_socket = upgrade_path_info->mutable_wifi_lan_socket();
wifi_lan_socket->set_ip_address(std::string({169, 254, 1, 1}));
wifi_lan_socket->set_wifi_port(kPort);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidateWifiLanUpgradeFrameWithFallbackInvalidPortFails) {
OfflineFrame offline_frame;
offline_frame.set_version(OfflineFrame::V1);
auto* v1_frame = offline_frame.mutable_v1();
v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION);
auto* negotiation = v1_frame->mutable_bandwidth_upgrade_negotiation();
negotiation->set_event_type(
BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE);
auto* upgrade_path_info = negotiation->mutable_upgrade_path_info();
upgrade_path_info->set_medium(UpgradePathInfo::WIFI_LAN);
auto* wifi_lan_socket = upgrade_path_info->mutable_wifi_lan_socket();
wifi_lan_socket->set_ip_address(std::string({192, 168, 1, 1}));
wifi_lan_socket->set_wifi_port(70000);
auto ret_value = EnsureValidOfflineFrame(offline_frame);
EXPECT_FALSE(ret_value.Ok());
}
TEST(OfflineFramesValidatorTest,
ValidatesAsFailWithNullBandwidthUpgradeNegotiationFrame) {
OfflineFrame offline_frame;
@@ -765,18 +1072,35 @@ TEST(OfflineFramesValidatorTest,
OfflineFrame offline_frame_2;
std::string wifi_direct_ssid{"DIRECT-A*-0123456789AB"};
std::string wifi_direct_pin_wrong_length = "abcefghijklmnopqrstuvwxyz";
std::string wifi_direct_pin_wrong_length = "01234567890123456";
std::string bytes = ForBwuWifiDirectPathAvailable(
wifi_direct_ssid, std::string(kWifiDirectPassword), kPort,
kWifiDirectFrequency, kSupportsDisablingEncryption,
std::string(kGateway), std::string(kWifiDirectDeviceName),
wifi_direct_pin_wrong_length);
kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway),
std::string(kWifiDirectDeviceName), wifi_direct_pin_wrong_length);
offline_frame_1.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame_1);
EXPECT_FALSE(ret_value.Ok());
std::string wifi_direct_ssid_64_length = "DIRECT-A0-" + std::string(54, 'A');
bytes = ForBwuWifiDirectPathAvailable(
wifi_direct_ssid_64_length, std::string(kWifiDirectPassword), kPort,
kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway),
std::string(kWifiDirectDeviceName), /*pin=*/"01234567890123456");
offline_frame_2.ParseFromString(bytes);
ret_value = EnsureValidOfflineFrame(offline_frame_2);
EXPECT_FALSE(ret_value.Ok());
std::string wifi_direct_pin_16_length = "0123456789012345";
bytes = ForBwuWifiDirectPathAvailable(
std::string(kWifiDirectSsid), std::string(kWifiDirectPassword), kPort,
kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway),
std::string(kWifiDirectDeviceName), wifi_direct_pin_16_length);
offline_frame_2.ParseFromString(bytes);
ret_value = EnsureValidOfflineFrame(offline_frame_2);
EXPECT_TRUE(ret_value.Ok());
std::string wifi_direct_ssid_wrong_length =
std::string{kWifiDirectSsid} + "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
std::string wifi_direct_device_name_wrong_length =
@@ -784,9 +1108,8 @@ TEST(OfflineFramesValidatorTest,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
bytes = ForBwuWifiDirectPathAvailable(
wifi_direct_ssid_wrong_length, std::string(kWifiDirectPassword), kPort,
kWifiDirectFrequency, kSupportsDisablingEncryption,
std::string(kGateway), wifi_direct_device_name_wrong_length,
std::string(kWifiDirectPin));
kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway),
wifi_direct_device_name_wrong_length, std::string(kWifiDirectPin));
offline_frame_2.ParseFromString(bytes);
ret_value = EnsureValidOfflineFrame(offline_frame_2);
@@ -807,9 +1130,8 @@ TEST(OfflineFramesValidatorTest,
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789";
std::string bytes = ForBwuWifiDirectPathAvailable(
std::string(kWifiDirectSsid), long_wifi_direct_password, kPort,
kWifiDirectFrequency, kSupportsDisablingEncryption,
std::string(kGateway), std::string(kWifiDirectDeviceName),
long_wifi_direct_pin);
kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway),
std::string(kWifiDirectDeviceName), long_wifi_direct_pin);
offline_frame_2.ParseFromString(bytes);
auto ret_value = EnsureValidOfflineFrame(offline_frame_2);
@@ -307,6 +307,9 @@ Status P2pClusterPcpHandler::StopAdvertisingImpl(ClientProxy* client) {
<< bluetooth_classic_advertiser_client_id_;
}
wifi_lan_medium_.StopAdvertising(client->GetAdvertisingServiceId());
wifi_lan_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
bluetooth_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
ble_medium_.StopAdvertising(client->GetAdvertisingServiceId());
ble_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
@@ -316,9 +319,6 @@ Status P2pClusterPcpHandler::StopAdvertisingImpl(ClientProxy* client) {
client->GetAdvertisingServiceId());
}
wifi_lan_medium_.StopAdvertising(client->GetAdvertisingServiceId());
wifi_lan_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::kEnableAwdl)) {
awdl_medium_.StopAdvertising(client->GetAdvertisingServiceId());
@@ -1238,8 +1238,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, this,
client_proxy, local_endpoint_id,
options.listening_endpoint_type));
client_proxy, options.listening_endpoint_type));
if (bluetooth_result.has_error()) {
LOG(WARNING)
<< "Failed to start listening for incoming connections on Bluetooth";
@@ -1266,8 +1265,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::BleConnectionAcceptedHandler2, this,
client_proxy, local_endpoint_id,
options.listening_endpoint_type))) {
client_proxy, options.listening_endpoint_type))) {
LOG(WARNING) << "Failed to start listening for incoming L2CAP "
"connections on ble";
} else {
@@ -1278,8 +1276,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::BleL2capConnectionAcceptedHandler,
this, client_proxy, local_endpoint_id,
options.listening_endpoint_type))) {
this, client_proxy, options.listening_endpoint_type))) {
LOG(WARNING) << "Failed to start listening for incoming L2CAP "
"connections on ble";
} else {
@@ -1295,8 +1292,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::BleConnectionAcceptedHandler2, this,
client_proxy, local_endpoint_id,
options.listening_endpoint_type))) {
client_proxy, options.listening_endpoint_type))) {
LOG(WARNING)
<< "Failed to start listening for incoming connections on ble_v2";
} else {
@@ -1307,8 +1303,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::BleConnectionAcceptedHandler, this,
client_proxy, local_endpoint_id,
options.listening_endpoint_type))) {
client_proxy, options.listening_endpoint_type))) {
LOG(WARNING)
<< "Failed to start listening for incoming connections on ble";
} else {
@@ -1327,7 +1322,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
std::string(service_id),
absl::bind_front(
&P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler, this,
client_proxy, local_endpoint_id, "",
client_proxy, std::string(local_endpoint_id),
options.listening_endpoint_type));
if (wifi_lan_result.has_error()) {
LOG(WARNING)
@@ -1680,8 +1675,7 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl(
restarted_mediums.push_back(AWDL);
operation_result_with_mediums.push_back(
GetOperationResultWithMediumByResultCode(
client, AWDL, update_index,
OperationResultCode::DETAIL_SUCCESS));
client, AWDL, update_index, OperationResultCode::DETAIL_SUCCESS));
} else {
ErrorOr<Medium> awdl_result =
StartAwdlDiscovery(client, std::string(service_id));
@@ -1739,15 +1733,8 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl(
}
void P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler(
ClientProxy* client, absl::string_view local_endpoint_info,
NearbyDevice::Type device_type, const std::string& service_id,
BluetoothSocket socket) {
if (!socket.IsValid()) {
LOG(WARNING) << "Invalid socket in accept callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
ClientProxy* client, NearbyDevice::Type device_type,
const std::string& service_id, BluetoothSocket socket) {
RunOnPcpHandlerThread(
"p2p-bt-on-incoming-connection",
[this, client, service_id, socket = std::move(socket), device_type]()
@@ -1782,8 +1769,7 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartBluetoothAdvertising(
service_id,
absl::bind_front(
&P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, this,
client, local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
client, NearbyDevice::Type::kConnectionsDevice));
if (accept_result.has_error()) {
error = {Error(accept_result.error().operation_result_code().value())};
}
@@ -1987,15 +1973,8 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BluetoothConnectImpl(
}
void P2pClusterPcpHandler::BleConnectionAcceptedHandler(
ClientProxy* client, absl::string_view local_endpoint_info,
NearbyDevice::Type device_type, BleSocket socket,
ClientProxy* client, NearbyDevice::Type device_type, BleSocket socket,
const std::string& service_id) {
if (!socket.IsValid()) {
LOG(WARNING) << "Invalid socket in accept callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
RunOnPcpHandlerThread(
"p2p-ble-on-incoming-connection",
[this, client, service_id, device_type,
@@ -2010,15 +1989,8 @@ void P2pClusterPcpHandler::BleConnectionAcceptedHandler(
}
void P2pClusterPcpHandler::BleL2capConnectionAcceptedHandler(
ClientProxy* client, absl::string_view local_endpoint_info,
NearbyDevice::Type device_type, BleL2capSocket socket,
ClientProxy* client, NearbyDevice::Type device_type, BleL2capSocket socket,
const std::string& service_id) {
if (!socket.IsValid()) {
LOG(WARNING) << "Invalid socket in accept L2CAP callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
RunOnPcpHandlerThread(
"p2p-ble-l2cap-on-incoming-connection",
[this, client, service_id, device_type,
@@ -2033,15 +2005,8 @@ void P2pClusterPcpHandler::BleL2capConnectionAcceptedHandler(
}
void P2pClusterPcpHandler::BleConnectionAcceptedHandler2(
ClientProxy* client, absl::string_view local_endpoint_info,
NearbyDevice::Type device_type, std::unique_ptr<mediums::BleSocket> socket,
const std::string& service_id) {
if (socket == nullptr || !socket->IsValid()) {
LOG(WARNING) << "Invalid socket in accept callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
ClientProxy* client, NearbyDevice::Type device_type,
std::unique_ptr<mediums::BleSocket> socket, const std::string& service_id) {
RunOnPcpHandlerThread(
"p2p-ble-on-incoming-connection",
[this, client, service_id, device_type, socket = std::move(socket)]()
@@ -2101,15 +2066,13 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartBleAdvertising(
service_id,
absl::bind_front(
&P2pClusterPcpHandler::BleConnectionAcceptedHandler2, this,
client, local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
client, NearbyDevice::Type::kConnectionsDevice));
} else {
ble_l2cap_result = ble_medium_.StartAcceptingL2capConnections(
service_id,
absl::bind_front(
&P2pClusterPcpHandler::BleL2capConnectionAcceptedHandler, this,
client, local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
client, NearbyDevice::Type::kConnectionsDevice));
}
}
@@ -2118,13 +2081,13 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartBleAdvertising(
ble_result = ble_medium_.StartAcceptingConnections(
service_id,
absl::bind_front(&P2pClusterPcpHandler::BleConnectionAcceptedHandler2,
this, client, local_endpoint_info.AsStringView(),
this, client,
NearbyDevice::Type::kConnectionsDevice));
} else {
ble_result = ble_medium_.StartAcceptingConnections(
service_id,
absl::bind_front(&P2pClusterPcpHandler::BleConnectionAcceptedHandler,
this, client, local_endpoint_info.AsStringView(),
this, client,
NearbyDevice::Type::kConnectionsDevice));
}
if (ble_result.has_error() && ble_l2cap_result.has_error()) {
@@ -2169,8 +2132,7 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartBleAdvertising(
service_id,
absl::bind_front(
&P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, this,
client, local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
client, NearbyDevice::Type::kConnectionsDevice));
if (accept_result.has_error()) {
LOG(WARNING)
<< "In BT StartBleAdvertising("
@@ -2430,23 +2392,16 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl(
}
void P2pClusterPcpHandler::AwdlConnectionAcceptedHandler(
ClientProxy* client, absl::string_view local_endpoint_id,
absl::string_view local_endpoint_info, NearbyDevice::Type device_type,
const std::string& service_id, AwdlSocket socket) {
if (!socket.IsValid()) {
LOG(WARNING) << "Invalid socket in accept callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
ClientProxy* client, const std::string& local_endpoint_id,
NearbyDevice::Type device_type, const std::string& service_id,
AwdlSocket socket) {
RunOnPcpHandlerThread(
"p2p-awdl-on-incoming-connection",
[this, client, local_endpoint_id, service_id, device_type,
socket = std::move(socket)]() RUN_ON_PCP_HANDLER_THREAD() mutable {
std::string remote_service_name = std::string(local_endpoint_id);
auto channel = std::make_unique<AwdlEndpointChannel>(
service_id, /*channel_name=*/remote_service_name, socket);
ByteArray remote_service_name_byte{remote_service_name};
service_id, /*channel_name=*/local_endpoint_id, socket);
ByteArray remote_service_name_byte{local_endpoint_id};
OnIncomingConnection(client, remote_service_name_byte,
std::move(channel), AWDL, device_type);
@@ -2454,23 +2409,16 @@ void P2pClusterPcpHandler::AwdlConnectionAcceptedHandler(
}
void P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler(
ClientProxy* client, absl::string_view local_endpoint_id,
absl::string_view local_endpoint_info, NearbyDevice::Type device_type,
const std::string& service_id, WifiLanSocket socket) {
if (!socket.IsValid()) {
LOG(WARNING) << "Invalid socket in accept callback("
<< absl::BytesToHexString(local_endpoint_info)
<< "), client=" << client->GetClientId();
return;
}
ClientProxy* client, const std::string& local_endpoint_id,
NearbyDevice::Type device_type, const std::string& service_id,
WifiLanSocket socket) {
RunOnPcpHandlerThread(
"p2p-wifi-on-incoming-connection",
[this, client, local_endpoint_id, service_id, device_type,
socket = std::move(socket)]() RUN_ON_PCP_HANDLER_THREAD() mutable {
std::string remote_service_name = std::string(local_endpoint_id);
auto channel = std::make_unique<WifiLanEndpointChannel>(
service_id, /*channel_name=*/remote_service_name, socket);
ByteArray remote_service_name_byte{remote_service_name};
service_id, /*channel_name=*/local_endpoint_id, socket);
ByteArray remote_service_name_byte{local_endpoint_id};
OnIncomingConnection(client, remote_service_name_byte,
std::move(channel), WIFI_LAN, device_type);
@@ -2490,7 +2438,6 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartAwdlAdvertising(
service_id,
absl::bind_front(&P2pClusterPcpHandler::AwdlConnectionAcceptedHandler,
this, client, local_endpoint_id,
local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
if (awdl_result.has_error()) {
LOG(WARNING)
@@ -2608,7 +2555,6 @@ ErrorOr<Medium> P2pClusterPcpHandler::StartWifiLanAdvertising(
service_id, nsd_service_info,
absl::bind_front(&P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler,
this, client, local_endpoint_id,
local_endpoint_info.AsStringView(),
NearbyDevice::Type::kConnectionsDevice));
if (wifi_lan_result.has_error()) {
LOG(WARNING) << "In StartWifiLanAdvertising("
@@ -40,10 +40,13 @@
#include "connections/implementation/mediums/bluetooth_classic.h"
#include "connections/implementation/mediums/bluetooth_radio.h"
#include "connections/implementation/mediums/mediums.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/mediums/wifi_direct.h"
#include "connections/implementation/mediums/wifi_hotspot.h"
#include "connections/implementation/mediums/wifi_lan.h"
#include "connections/implementation/pcp.h"
#include "connections/implementation/webrtc_state.h"
#include "connections/implementation/wifi_lan_service_info.h"
#include "connections/medium_selector.h"
#include "connections/out_of_band_connection_metadata.h"
#include "connections/power_level.h"
@@ -54,13 +57,10 @@
#include "internal/platform/ble.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/nsd_service_info.h"
#include "internal/platform/wifi_lan.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/pcp.h"
#include "connections/implementation/wifi_lan_service_info.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/expected.h"
#include "internal/platform/nsd_service_info.h"
#include "internal/platform/wifi_lan.h"
namespace nearby {
namespace connections {
@@ -192,7 +192,6 @@ class P2pClusterPcpHandler : public BasePcpHandler {
const std::string& service_id,
BluetoothDevice& device);
void BluetoothConnectionAcceptedHandler(ClientProxy* client,
absl::string_view local_endpoint_info,
NearbyDevice::Type device_type,
const std::string& service_id,
BluetoothSocket socket);
@@ -232,19 +231,16 @@ class P2pClusterPcpHandler : public BasePcpHandler {
bool fast_advertisement);
void BleLegacyDeviceDiscoveredHandler();
void BleConnectionAcceptedHandler(ClientProxy* client,
absl::string_view local_endpoint_info,
NearbyDevice::Type device_type,
BleSocket socket,
const std::string& service_id);
void BleL2capConnectionAcceptedHandler(ClientProxy* client,
absl::string_view local_endpoint_info,
NearbyDevice::Type device_type,
BleL2capSocket socket,
const std::string& service_id);
// The refactor version of BleConnectionAcceptedHandler() and
// BleL2capConnectionAcceptedHandler above.
void BleConnectionAcceptedHandler2(ClientProxy* client,
absl::string_view local_endpoint_info,
NearbyDevice::Type device_type,
std::unique_ptr<mediums::BleSocket> socket,
const std::string& service_id);
@@ -265,8 +261,7 @@ class P2pClusterPcpHandler : public BasePcpHandler {
void AwdlServiceLostHandler(ClientProxy* client, NsdServiceInfo service_info,
const std::string& service_id);
void AwdlConnectionAcceptedHandler(ClientProxy* client,
absl::string_view local_endpoint_id,
absl::string_view local_endpoint_info,
const std::string& local_endpoint_id,
NearbyDevice::Type device_type,
const std::string& service_id,
AwdlSocket socket);
@@ -290,8 +285,7 @@ class P2pClusterPcpHandler : public BasePcpHandler {
NsdServiceInfo service_info,
const std::string& service_id);
void WifiLanConnectionAcceptedHandler(ClientProxy* client,
absl::string_view local_endpoint_id,
absl::string_view local_endpoint_info,
const std::string& local_endpoint_id,
NearbyDevice::Type device_type,
const std::string& service_id,
WifiLanSocket socket);
@@ -157,6 +157,7 @@ message ConnectionResponseFrame {
optional int32 safe_to_disconnect_version = 7;
optional LocationHint location_hint = 8;
optional int32 keep_alive_timeout_millis = 9;
optional string wifi_direct_device_name = 10;
}
message PayloadTransferFrame {
-1
View File
@@ -25,7 +25,6 @@ cc_library(
visibility = [
"//internal/account:__subpackages__",
"//internal/platform:__subpackages__",
"//internal/test:__pkg__",
"//location/nearby/sharing/lib:__subpackages__",
"//sharing:__subpackages__",
],
+4
View File
@@ -142,4 +142,8 @@ std::optional<size_t> Files::GetAvailableDiskSpaceInBytes(
return std::nullopt;
}
bool Files::IsAbsolutePath(const FilePath& path) {
return path.path_.is_absolute();
}
} // namespace nearby
+5
View File
@@ -75,6 +75,11 @@ class Files {
// determined.
static std::optional<size_t> GetAvailableDiskSpaceInBytes(
const FilePath& path);
// Returns true if the path is an absolute path.
// On Posix systems, this is a path that starts with a `/`.
// On Windows, this is a path that starts with a drive letter or a UNC path.
static bool IsAbsolutePath(const FilePath& path);
};
} // namespace nearby
+16
View File
@@ -46,5 +46,21 @@ TEST(FilesTest, CreateHardLinkSuccess) {
Files::RemoveFile(target);
}
#if defined(_WIN32)
TEST(FilesTest, IsAbsolutePathWindows) {
EXPECT_TRUE(Files::IsAbsolutePath(FilePath("C:\\Users\\test\\file.txt")));
EXPECT_TRUE(Files::IsAbsolutePath(FilePath("\\\\server\\share\\file.txt")));
EXPECT_FALSE(Files::IsAbsolutePath(FilePath("file.txt")));
EXPECT_FALSE(Files::IsAbsolutePath(FilePath("C:Users\\test\\file.txt")));
}
#endif
#if defined(__linux__) || defined(__APPLE__)
TEST(FilesTest, IsAbsolutePathPosix) {
EXPECT_TRUE(Files::IsAbsolutePath(FilePath("/Users/test/file.txt")));
EXPECT_FALSE(Files::IsAbsolutePath(FilePath("file.txt")));
}
#endif
} // namespace
} // namespace nearby
+1 -1
View File
@@ -114,7 +114,7 @@ std::optional<std::vector<uint8_t>> Aead::Open(
size_t output_length;
if (!Open(ciphertext, nonce, additional_data, ret.data(), &output_length,
max_output_length)) {
return absl::nullopt;
return std::nullopt;
}
ret.resize(output_length);
+5 -5
View File
@@ -176,7 +176,7 @@ std::optional<size_t> Encryptor::Crypt(bool do_encrypt,
if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr,
reinterpret_cast<const uint8_t*>(key.data()),
iv_.data(), do_encrypt)) {
return absl::nullopt;
return std::nullopt;
}
// Encrypting needs a block size of space to allow for any padding.
@@ -184,13 +184,13 @@ std::optional<size_t> Encryptor::Crypt(bool do_encrypt,
int out_len;
if (!EVP_CipherUpdate(ctx.get(), output.data(), &out_len, input.data(),
input.size()))
return absl::nullopt;
return std::nullopt;
// Write out the final block plus padding (if any) to the end of the data
// just written.
int tail_len;
if (!EVP_CipherFinal_ex(ctx.get(), output.data() + out_len, &tail_len))
return absl::nullopt;
return std::nullopt;
out_len += tail_len;
DCHECK_LE(out_len, static_cast<int>(output.size()));
@@ -202,13 +202,13 @@ std::optional<size_t> Encryptor::CryptCTR(bool do_encrypt,
absl::Span<uint8_t> output) {
if (iv_.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Counter value not set in CTR mode.";
return absl::nullopt;
return std::nullopt;
}
AES_KEY aes_key;
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()),
key_->key().size() * 8, &aes_key) != 0) {
return absl::nullopt;
return std::nullopt;
}
uint8_t ecount_buf[AES_BLOCK_SIZE] = {0};
+9 -10
View File
@@ -89,28 +89,27 @@ TEST(NearbyFlags, SetFlagReader) {
auto flag_reader = std::make_unique<::testing::NiceMock<MockFlagReader>>();
NearbyFlags::GetInstance().SetFlagReader(*flag_reader.get());
EXPECT_CALL(*flag_reader, GetBoolFlag(::testing::_))
.WillOnce(::testing::Invoke([=](const flags::Flag<bool>& flag) {
.WillOnce([=](const flags::Flag<bool>& flag) {
return kTestBoolFlagTestValue;
}));
});
EXPECT_EQ(NearbyFlags::GetInstance().GetBoolFlag(kTestBoolFlag),
kTestBoolFlagTestValue);
EXPECT_CALL(*flag_reader, GetInt64Flag(::testing::_))
.WillOnce(::testing::Invoke([=](const flags::Flag<int64_t>& flag) {
.WillOnce([=](const flags::Flag<int64_t>& flag) {
return kTestInt64FlagTestValue;
}));
});
EXPECT_EQ(NearbyFlags::GetInstance().GetInt64Flag(kTestInt64Flag),
kTestInt64FlagTestValue);
EXPECT_CALL(*flag_reader, GetDoubleFlag(::testing::_))
.WillOnce(::testing::Invoke([=](const flags::Flag<double>& flag) {
.WillOnce([=](const flags::Flag<double>& flag) {
return kTestDoubleFlagTestValue;
}));
});
EXPECT_EQ(NearbyFlags::GetInstance().GetDoubleFlag(kTestDoubleFlag),
kTestDoubleFlagTestValue);
EXPECT_CALL(*flag_reader, GetStringFlag(::testing::_))
.WillOnce(
::testing::Invoke([=](const flags::Flag<absl::string_view>& flag) {
return std::string(kTestStringFlagTestValue);
}));
.WillOnce([=](const flags::Flag<absl::string_view>& flag) {
return std::string(kTestStringFlagTestValue);
});
EXPECT_EQ(NearbyFlags::GetInstance().GetStringFlag(kTestStringFlag),
kTestStringFlagTestValue);
}
-4
View File
@@ -80,8 +80,6 @@ cc_library(
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
@@ -323,7 +321,6 @@ cc_library(
":types",
":uuid",
"//connections/implementation/flags:connections_flags",
"//internal/base",
"//internal/flags:nearby_flags",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:platform",
@@ -374,7 +371,6 @@ cc_library(
":mac_address",
":types",
":uuid",
"//internal/base",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:webrtc_platform",
"//internal/platform/implementation:wifi_utils",
-73
View File
@@ -19,7 +19,6 @@
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/bluetooth_classic.h"
@@ -57,11 +56,6 @@ MediumSocket* BluetoothSocket::CreateVirtualSocket(
}
BluetoothClassicMedium::~BluetoothClassicMedium() {
LOG(INFO) << "~BluetoothClassicMedium: observer_list_ size: "
<< observer_list_.size();
if (!observer_list_.empty()) {
impl_->RemoveObserver(this);
}
StopDiscovery();
LOG(INFO) << "eof ~BluetoothClassicMedium";
}
@@ -160,71 +154,4 @@ bool BluetoothClassicMedium::StopDiscovery() {
return impl_->StopDiscovery();
}
void BluetoothClassicMedium::AddObserver(Observer* observer) {
LOG(INFO) << "BT AddObserver; impl=" << &GetImpl();
MutexLock lock(&mutex_);
if (observer_list_.empty()) {
impl_->AddObserver(this);
}
observer_list_.AddObserver(observer);
LOG(INFO) << "BT AddObserver done";
}
void BluetoothClassicMedium::RemoveObserver(Observer* observer) {
LOG(INFO) << "BT RemoveObserver; impl=" << &GetImpl();
MutexLock lock(&mutex_);
observer_list_.RemoveObserver(observer);
if (observer_list_.empty()) {
impl_->RemoveObserver(this);
}
LOG(INFO) << "BT RemoveObserver done";
}
// api::BluetoothClassicMedium::Observer methods
void BluetoothClassicMedium::DeviceAdded(api::BluetoothDevice& device) {
VLOG(1) << "BT DeviceAdded; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString();
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceAdded(bt_device);
}
}
void BluetoothClassicMedium::DeviceRemoved(api::BluetoothDevice& device) {
VLOG(1) << "BT DeviceRemoved; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString();
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceRemoved(bt_device);
}
}
void BluetoothClassicMedium::DeviceAddressChanged(
api::BluetoothDevice& device, absl::string_view old_address) {
VLOG(1) << "BT DeviceAddressChanged; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", old_address=" << old_address;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceAddressChanged(bt_device, old_address);
}
}
void BluetoothClassicMedium::DevicePairedChanged(api::BluetoothDevice& device,
bool new_paired_status) {
VLOG(1) << "BT DevicePairedChanged; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", status=" << new_paired_status;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DevicePairedChanged(bt_device, new_paired_status);
}
}
void BluetoothClassicMedium::DeviceConnectedStateChanged(
api::BluetoothDevice& device, bool connected) {
VLOG(1) << "BT DeviceConnectedStateChanged: name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", connected=" << connected;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceConnectedStateChanged(bt_device, connected);
}
}
} // namespace nearby
+3 -50
View File
@@ -26,7 +26,6 @@
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "internal/base/observer_list.h"
#include "internal/platform/blocking_queue_stream.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
@@ -157,7 +156,7 @@ class BluetoothServerSocket final {
BluetoothServerSocket& operator=(const BluetoothServerSocket&) = default;
~BluetoothServerSocket() = default;
explicit BluetoothServerSocket(
std::unique_ptr<api::BluetoothServerSocket> socket)
std::shared_ptr<api::BluetoothServerSocket> socket)
: impl_(std::move(socket)) {}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
@@ -223,7 +222,7 @@ class BluetoothPairing final {
// Container of operations that can be performed over the Bluetooth Classic
// medium.
class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
class BluetoothClassicMedium {
public:
using Platform = api::ImplementationPlatform;
struct DiscoveryCallback {
@@ -246,39 +245,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
BluetoothDevice device;
};
class Observer {
public:
virtual ~Observer() = default;
// Called when a new `device` is added. The `device` parameter becomes
// invalid after the call.
virtual void DeviceAdded(BluetoothDevice& device) {}
// Called when `device` is removed. The `device` parameter becomes invalid
// after the call.
virtual void DeviceRemoved(BluetoothDevice& device) {}
// Called when the address of `device` changed due to pairing. The
// `device` parameter becomes invalid after the call.
virtual void DeviceAddressChanged(BluetoothDevice& device,
absl::string_view old_address) {}
// Called when the paired property of `device` changed. The `device`
// parameter becomes invalid after the call.
virtual void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) {}
// Called when `device` has connected or disconnected. The `device`
// parameter becomes invalid after the call.
virtual void DeviceConnectedStateChanged(BluetoothDevice& device,
bool connected) {}
};
explicit BluetoothClassicMedium(BluetoothAdapter& adapter)
: impl_(Platform::CreateBluetoothClassicMedium(adapter.GetImpl())),
adapter_(adapter) {}
~BluetoothClassicMedium() override;
virtual ~BluetoothClassicMedium();
// NOTE(DiscoveryCallback):
// BluetoothDevice is a proxy object created as a result of BT discovery.
@@ -353,23 +324,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
return BluetoothDevice(impl_->GetRemoteDevice(mac_address));
}
// Adds an observer. `observer` must be valid until RemoveObserver is called,
// or BluetoothClassicMedium is destroyed.
void AddObserver(Observer* observer);
// Removes an observer. It's OK to remove an unregistered observer.
void RemoveObserver(Observer* observer);
// api::BluetoothClassicMedium::Observer methods
void DeviceAdded(api::BluetoothDevice& device) override;
void DeviceRemoved(api::BluetoothDevice& device) override;
void DeviceAddressChanged(api::BluetoothDevice& device,
absl::string_view old_address) override;
void DevicePairedChanged(api::BluetoothDevice& device,
bool new_paired_status) override;
void DeviceConnectedStateChanged(api::BluetoothDevice& device,
bool connected) override;
private:
Mutex mutex_;
std::unique_ptr<api::BluetoothClassicMedium> impl_;
@@ -379,7 +333,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
devices_ ABSL_GUARDED_BY(mutex_);
DiscoveryCallback discovery_callback_ ABSL_GUARDED_BY(mutex_);
bool discovery_enabled_ ABSL_GUARDED_BY(mutex_) = false;
ObserverList<Observer> observer_list_;
};
} // namespace nearby
@@ -49,39 +49,6 @@ constexpr FeatureFlags kTestCases[] = {
},
};
class BluetoothClassicMediumObserver
: public BluetoothClassicMedium ::Observer {
public:
explicit BluetoothClassicMediumObserver(
CountDownLatch* device_added_latch, CountDownLatch* device_removed_latch,
CountDownLatch* device_paired_changed_latch)
: device_added_latch_(device_added_latch),
device_removed_latch_(device_removed_latch),
device_paired_changed_latch_(device_paired_changed_latch) {}
void DeviceAdded(BluetoothDevice& device) override {
if (!device_added_latch_) return;
device_added_latch_->CountDown();
}
void DeviceRemoved(BluetoothDevice& device) override {
if (!device_removed_latch_) return;
device_removed_latch_->CountDown();
}
void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) override {
if (!device_paired_changed_latch_) return;
paired_status_ = new_paired_status;
device_paired_changed_latch_->CountDown();
}
CountDownLatch* device_added_latch_;
CountDownLatch* device_removed_latch_;
CountDownLatch* device_paired_changed_latch_;
bool paired_status_ = false;
};
class BluetoothClassicMediumTest
: public ::testing::TestWithParam<FeatureFlags> {
protected:
@@ -341,11 +308,6 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) {
adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable);
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
CountDownLatch device_added_latch(1);
CountDownLatch device_removed_latch(1);
BluetoothClassicMediumObserver observer(&device_added_latch,
&device_removed_latch, nullptr);
bt_a_->AddObserver(&observer);
bt_a_->StartDiscovery(DiscoveryCallback{
.device_discovered_cb =
@@ -365,21 +327,15 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) {
EXPECT_EQ(adapter_b_->GetScanMode(),
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_added_latch.Await(absl::Milliseconds(1000)).result());
adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled);
EXPECT_FALSE(adapter_b_->IsEnabled());
EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_removed_latch.Await(absl::Milliseconds(1000)).result());
}
TEST_F(BluetoothClassicMediumTest, DiscoveryCallbackAfterStopDiscovery) {
SingleThreadExecutor executor;
adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable);
CountDownLatch found_latch(1);
CountDownLatch device_added_latch(1);
BluetoothClassicMediumObserver observer(&device_added_latch, nullptr,
nullptr);
bt_a_->AddObserver(&observer);
bt_a_->StartDiscovery(DiscoveryCallback{
.device_discovered_cb =
@@ -397,7 +353,6 @@ TEST_F(BluetoothClassicMediumTest, DiscoveryCallbackAfterStopDiscovery) {
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
bt_a_->StopDiscovery();
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_added_latch.Await(absl::Milliseconds(1000)).result());
executor.Shutdown();
}
@@ -491,10 +446,6 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) {
CountDownLatch paired_latch(1);
CountDownLatch initiated_latch(1);
CountDownLatch error_latch(1);
CountDownLatch device_paired_latch(1);
BluetoothClassicMediumObserver observer(nullptr, nullptr,
&device_paired_latch);
bt_a_->AddObserver(&observer);
EXPECT_TRUE(bluetooth_pairing->InitiatePairing({
.on_paired_cb = [&]() { paired_latch.CountDown(); },
.on_pairing_error_cb =
@@ -520,8 +471,6 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) {
// Finishes pairing with remote device.
EXPECT_TRUE(bluetooth_pairing->FinishPairing(received_passkey));
paired_latch.Await();
device_paired_latch.Await();
EXPECT_TRUE(observer.paired_status_);
EXPECT_TRUE(bluetooth_pairing->IsPaired());
// Unpairs with remote device.
+3
View File
@@ -190,6 +190,9 @@ cc_library(
],
deps = [
] + select({
"@platforms//os:platform_macos": [
"//internal/platform/implementation/apple",
],
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
@@ -221,6 +221,7 @@ objc_library(
"//internal/platform/implementation/apple/Mediums/BLE/Sockets:Peripheral",
"//third_party/apple_frameworks:CoreBluetooth",
"//third_party/apple_frameworks:Foundation",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
@@ -386,6 +386,10 @@ static const int kMaxAdvertisementLengthOnIOS = 23;
[_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound];
return;
}
if (request.offset > value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
request.value =
[value subdataWithRange:NSMakeRange(request.offset, value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
@@ -24,6 +24,7 @@
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMConnection.h"
enum { kL2CAPPacketLength = 4 };
static const NSUInteger kGNCBLEL2CAPMaxFrameLength = 5 * 1024 * 1024; // 5 MB
static const CGFloat kRequestDataConnectionDelayInSeconds = 0.0;
static const UInt8 kRequestDataConnectionTimeoutInSeconds = 5;
@@ -266,6 +267,20 @@ static NSData *PrefixLengthData(NSData *data) {
}
_expectedDataLength = CFSwapInt32BigToHost(
*(int *)([[data subdataWithRange:NSMakeRange(0, kL2CAPPacketLength)] bytes]));
if (_expectedDataLength == 0 || _expectedDataLength > kGNCBLEL2CAPMaxFrameLength) {
GNCLoggerError(@"[NEARBY] Rejecting L2CAP frame: declared length %lu out of range "
@"(max %lu); closing.",
(unsigned long)_expectedDataLength,
(unsigned long)kGNCBLEL2CAPMaxFrameLength);
_expectedDataLength = 0;
[_stream close];
if (_connectionHandlers.disconnectedHandler) {
dispatch_async(_callbackQueue, ^{
_connectionHandlers.disconnectedHandler();
});
}
return nil;
}
}
NSUInteger realDataLength = data.length - kL2CAPPacketLength;
if (realDataLength < _expectedDataLength) {
@@ -74,6 +74,9 @@ enum { READ_BUFFER_SIZE = 409600 };
/// Whether the stream is closed.
BOOL _closed;
/// Buffer for reading data from the input stream.
uint8_t _readBuffer[READ_BUFFER_SIZE];
}
#pragma mark Public
@@ -319,12 +322,11 @@ enum { READ_BUFFER_SIZE = 409600 };
/// Receives data from device and invokes |_receivedDataBlock|.
- (void)receiveStreamData {
dispatch_assert_queue_debug(_streamQueue);
uint8_t readBuffer[READ_BUFFER_SIZE];
NSInteger bytesRead = [self.inputStream read:readBuffer maxLength:READ_BUFFER_SIZE];
NSInteger bytesRead = [self.inputStream read:_readBuffer maxLength:READ_BUFFER_SIZE];
if (bytesRead > 0) {
NSMutableData *data = [NSMutableData data];
[data appendBytes:readBuffer length:(NSUInteger)bytesRead];
[data appendBytes:_readBuffer length:(NSUInteger)bytesRead];
if (_verboseLoggingEnabled) {
GNCLoggerDebug(@"[NEARBY] Stream data from device of length %@", @(data.length));
@@ -140,7 +140,11 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
if (![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
// IntroductionFrame.service_id_hash. We MUST bounds-check before
// -subdataWithRange:, otherwise a short follow-up packet throws
// NSRangeException on CoreBluetooth's dispatch queue -> objc_terminate.
if (data.length < prefixLength ||
![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong data packet and discarded");
return;
}
@@ -78,6 +78,11 @@ NSData *_Nullable GNCMParseBLEFramesIntroductionPacket(NSData *_Nullable data) {
::location::nearby::mediums::SocketVersion::V2 &&
socket_control_frame.introduction().has_service_id_hash()) {
std::string service_id_hash = socket_control_frame.introduction().service_id_hash();
// service_id_hash is attacker-supplied; clamp to the protocol-defined
// 3-byte length so it cannot be used to inflate prefixLength downstream.
if (service_id_hash.size() != GNCMBleAdvertisementLengthServiceIDHash) {
return nil;
}
return [NSData dataWithBytes:service_id_hash.data() length:service_id_hash.length()];
}
}
@@ -111,15 +111,17 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
- (void)addPeripheralServiceManager:(GNSPeripheralServiceManager *)peripheralServiceManager
bleServiceAddedCompletion:(GNSErrorHandler)completion {
[_peripheralServiceManagers setObject:peripheralServiceManager
forKey:peripheralServiceManager.serviceUUID];
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
if (_started) {
[self addBleServiceForServiceManager:peripheralServiceManager];
}
// Update all advertised services to make sure that the right services are advertised in case
// all BLE services were already added.
[self updateAdvertisedServices];
dispatch_async(_queue, ^{
[self->_peripheralServiceManagers setObject:peripheralServiceManager
forKey:peripheralServiceManager.serviceUUID];
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
if (self->_started) {
[self addBleServiceForServiceManager:peripheralServiceManager];
}
// Update all advertised services to make sure that the right services are advertised in case
// all BLE services were already added.
[self updateAdvertisedServices];
});
}
- (void)start {
@@ -211,18 +213,24 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
- (void)removePeripheralServiceManagerForServiceUUID:(CBUUID *)serviceUUID
bleServiceRemovedCompletion:(GNSErrorHandler)completion {
GNSPeripheralServiceManager *peripheralServiceManager =
[_peripheralServiceManagers objectForKey:serviceUUID];
if (peripheralServiceManager == nil) {
completion(nil);
return;
}
[_cbPeripheralManager removeService:peripheralServiceManager.cbService];
[_peripheralServiceManagers removeObjectForKey:serviceUUID];
[peripheralServiceManager didRemoveCBService];
dispatch_async(_queue, ^{
GNSPeripheralServiceManager *peripheralServiceManager =
[self->_peripheralServiceManagers objectForKey:serviceUUID];
if (peripheralServiceManager == nil) {
if (completion) {
completion(nil);
}
return;
}
[self->_cbPeripheralManager removeService:peripheralServiceManager.cbService];
[self->_peripheralServiceManagers removeObjectForKey:serviceUUID];
[peripheralServiceManager didRemoveCBService];
[self updateAdvertisedServices];
completion(nil);
[self updateAdvertisedServices];
if (completion) {
completion(nil);
}
});
}
- (void)removeAllBleServices {
File diff suppressed because it is too large Load Diff
@@ -140,6 +140,32 @@ static const NSTimeInterval kTestTimeout = 1.0;
XCTAssertNil(realData);
}
- (void)testExtractRealDataFromData_oversizedData {
_connection = [self createConnectionWithIncoming:YES];
XCTestExpectation *disconnectionExpectation =
[self expectationWithDescription:@"disconnection handler"];
_connection.connectionHandlers = [GNCMConnectionHandlers
payloadHandler:^(NSData *data) {
XCTFail(@"Unexpected payload");
}
disconnectedHandler:^{
[disconnectionExpectation fulfill];
}];
// 6 MB frame length
uint32_t oversizedLength = 6 * 1024 * 1024;
uint32_t lengthBigEndian = CFSwapInt32HostToBig(oversizedLength);
NSMutableData *prefixData = [NSMutableData dataWithCapacity:sizeof(uint32_t)];
[prefixData appendBytes:&lengthBigEndian length:sizeof(uint32_t)];
NSData *realData = [_connection extractRealDataFromData:prefixData];
XCTAssertNil(realData);
XCTAssertEqual(_connection.expectedDataLength, 0);
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
}
- (void)testExtractRealDataFromData_moreThanExpectedData {
_connection = [self createConnectionWithIncoming:YES];
NSData *testData = [self createDataWithLength:10];
@@ -47,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
/** Expectation fulfilled when peripheral responds to a request with an error. */
@property(nonatomic, readonly) XCTestExpectation *respondToRequestErrorExpectation;
/** The last response result. */
@property(nonatomic, assign) CBATTError lastResponseResult;
/** Expectation fulfilled when peripheral unpublishes an L2CAP channel. */
@property(nonatomic, readonly) XCTestExpectation *unpublishExpectation;
@@ -117,6 +120,20 @@ NS_ASSUME_NONNULL_BEGIN
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic;
/**
* Simulates a read request event with an offset.
*
* Creates a fake read request with the given offset for the given service and characteristic UUIDs
* and calls the @c gnc_peripheralManager:didReceiveReadRequest: delegate method.
*
* @param service The service UUID of the characteristic to read from.
* @param characteristic The characteristic UUID to read from.
* @param offset The offset to read from.
*/
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic
offset:(NSUInteger)offset;
@end
NS_ASSUME_NONNULL_END
@@ -37,6 +37,9 @@
// Keep a strong reference to the service.
@property(readwrite, nonatomic) CBService *service;
// Change property to readwrite for tests.
@property(readwrite, nonatomic) NSUInteger offset;
- (instancetype)initWithService:(CBUUID *)service characteristic:(CBUUID *)characteristic;
@end
@@ -115,6 +118,7 @@ static const uint16_t kPSM = 192;
}
- (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result {
self.lastResponseResult = result;
if (result == CBATTErrorSuccess) {
[_respondToRequestSuccessExpectation fulfill];
return;
@@ -176,6 +180,15 @@ static const uint16_t kPSM = 192;
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
}
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic
offset:(NSUInteger)offset {
CBATTRequest *request = [[CBATTRequest alloc] initWithService:service
characteristic:characteristic];
request.offset = offset;
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
}
- (void)setDelegate:(id<CBPeripheralManagerDelegate>)delegate {
self.peripheralDelegate = (id<GNCPeripheralManagerDelegate>)delegate;
}
@@ -241,4 +241,33 @@ static const NSTimeInterval kTimeout = 1.0;
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testReceiveShortDataPacketAfterIntro {
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
serviceID:nil
expectedIntroPacket:YES
callbackQueue:_callbackQueue];
NSData *introPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
// Receive the intro packet first to set `_serviceIDHash`.
[_fakeSocket simulateSocketDidReceiveData:introPacket];
// Receive a data packet that is shorter than the service ID hash length.
// This should not crash; it should just be discarded.
NSData *shortPacket = [@"ab" dataUsingEncoding:NSUTF8StringEncoding];
XCTestExpectation *expectation = [self expectationWithDescription:@"Payload handler not called"];
expectation.inverted = YES;
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
handlers.payloadHandler = ^(NSData *data) {
[expectation fulfill];
};
_connection.connectionHandlers = handlers;
[_fakeSocket simulateSocketDidReceiveData:shortPacket];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
@end
@@ -45,6 +45,18 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
XCTAssertEqualObjects(parsedHash, serviceIDHash);
}
- (void)testParseBLEFramesIntroductionPacketFailure_InvalidHashLength {
// Too long hash (4 bytes, protocol expects 3 bytes)
NSData *longHash = [@"1234" dataUsingEncoding:NSUTF8StringEncoding];
NSData *longPacket = GNCMGenerateBLEFramesIntroductionPacket(longHash);
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(longPacket));
// Too short hash (2 bytes, protocol expects 3 bytes)
NSData *shortHash = [@"12" dataUsingEncoding:NSUTF8StringEncoding];
NSData *shortPacket = GNCMGenerateBLEFramesIntroductionPacket(shortHash);
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(shortPacket));
}
- (void)testParseBLEFramesIntroductionPacketFailure_NilData {
NSData *parsedHash = GNCMParseBLEFramesIntroductionPacket(nil);
XCTAssertNil(parsedHash);
@@ -57,7 +57,10 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSString *)getBonjourServiceNameFromEndpoint:(nw_endpoint_t)endpoint {
const char *name = nw_endpoint_get_bonjour_service_name(endpoint);
return name ? @(name) : nil;
if (name == NULL) return nil;
// @() returns nil on non-UTF-8 input; the wire format does not guarantee UTF-8.
// Round-trip through Latin-1 so callers always get a non-nil NSString.
return @(name) ?: [NSString stringWithCString:name encoding:NSISOLatin1StringEncoding];
}
@end
@@ -63,11 +63,14 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
block:^bool(const char *key, const nw_txt_record_find_key_t found,
const uint8_t *value, const size_t value_len) {
if (found == nw_txt_record_find_key_non_empty_value) {
NSString *keyString = @(key);
NSString *valueString =
[[NSString alloc] initWithBytes:value
length:value_len
encoding:NSUTF8StringEncoding];
[txtRecords setValue:valueString forKey:@(key)];
if (keyString != nil && valueString != nil) {
[txtRecords setObject:valueString forKey:keyString];
}
}
return YES;
}];
@@ -231,6 +234,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:new_result];
NSString *name = [browseResultWrapper
getBonjourServiceNameFromEndpoint:endpoint];
if (name == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable name.");
break;
}
NSDictionary<NSString *, NSString *> *txtRecords =
GNCTXTRecordForBrowseResult(new_result);
serviceFoundHandler(name, txtRecords);
@@ -250,6 +258,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:old_result];
NSString *oldName = [browseResultWrapper
getBonjourServiceNameFromEndpoint:old_endpoint];
if (oldName == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable old name.");
break;
}
NSDictionary<NSString *, NSString *> *oldTXTRecords =
GNCTXTRecordForBrowseResult(old_result);
serviceLostHandler(oldName, oldTXTRecords);
@@ -258,6 +271,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:new_result];
NSString *newName = [browseResultWrapper
getBonjourServiceNameFromEndpoint:new_endpoint];
if (newName == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable new name.");
break;
}
NSDictionary<NSString *, NSString *> *newTXTRecords =
GNCTXTRecordForBrowseResult(new_result);
serviceFoundHandler(newName, newTXTRecords);
@@ -276,6 +294,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:old_result];
NSString *name = [browseResultWrapper
getBonjourServiceNameFromEndpoint:endpoint];
if (name == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable name.");
break;
}
NSDictionary<NSString *, NSString *> *txtRecords =
GNCTXTRecordForBrowseResult(old_result);
serviceLostHandler(name, txtRecords);
@@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic) nw_browse_result_change_t getChangesFromResult;
@property(nonatomic, nullable) nw_endpoint_t endpointFromResultResult;
@property(nonatomic, nullable) NSString *getBonjourServiceNameFromEndpointResult;
@property(nonatomic) BOOL returnNilServiceName;
@end
@@ -67,6 +67,9 @@ NS_ASSUME_NONNULL_BEGIN
}
- (nullable NSString *)getBonjourServiceNameFromEndpoint:(nw_endpoint_t)endpoint {
if (self.returnNilServiceName) {
return nil;
}
return self.getBonjourServiceNameFromEndpointResult ?: @"FakeService";
}
@@ -53,16 +53,16 @@ NS_ASSUME_NONNULL_BEGIN
* @param serviceName The name of the service found.
* @param txtRecords The TXT records of the service found.
*/
- (void)triggerServiceFound:(NSString*)serviceName
txtRecords:(NSDictionary<NSString*, NSString*>*)txtRecords;
- (void)triggerServiceFound:(nullable NSString*)serviceName
txtRecords:(nullable NSDictionary<NSString*, NSString*>*)txtRecords;
/**
* Triggers the service lost handler with the given service info.
*
* @param serviceName The name of the service lost.
* @param txtRecords The TXT records of the service lost.
*/
- (void)triggerServiceLost:(NSString*)serviceName
txtRecords:(NSDictionary<NSString*, NSString*>*)txtRecords;
- (void)triggerServiceLost:(nullable NSString*)serviceName
txtRecords:(nullable NSDictionary<NSString*, NSString*>*)txtRecords;
@end
@@ -122,15 +122,15 @@
return serverSocket;
}
- (void)triggerServiceFound:(NSString *)serviceName
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
- (void)triggerServiceFound:(nullable NSString *)serviceName
txtRecords:(nullable NSDictionary<NSString *, NSString *> *)txtRecords {
if (self.serviceFoundHandler) {
self.serviceFoundHandler(serviceName, txtRecords);
}
}
- (void)triggerServiceLost:(NSString *)serviceName
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
- (void)triggerServiceLost:(nullable NSString *)serviceName
txtRecords:(nullable NSDictionary<NSString *, NSString *> *)txtRecords {
if (self.serviceLostHandler) {
self.serviceLostHandler(serviceName, txtRecords);
}
@@ -67,4 +67,17 @@
XCTAssertEqualObjects(results[@"key2"], @"value2");
}
- (void)testGetBonjourServiceName_InvalidUTF8 {
GNCNWBrowseResultImpl *browseResult = [[GNCNWBrowseResultImpl alloc] init];
const char *raw_invalid = "\xc3\x28"
"abc";
nw_endpoint_t endpoint =
nw_endpoint_create_bonjour_service(raw_invalid, "_servicetype._tcp", "local.");
XCTAssertNotNil(endpoint);
NSString *serviceName = [browseResult getBonjourServiceNameFromEndpoint:endpoint];
XCTAssertNotNil(serviceName);
XCTAssertEqualObjects(serviceName, @"(abc");
}
@end
@@ -61,7 +61,6 @@ static NSString *const kHostAddress = @"127.0.0.1";
_mockConnectionImpl = OCMClassMock([GNCNWConnectionImpl class]);
}
- (void)testGNCNWFrameworkCanBeInstantiated {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
XCTAssertNotNil(framework);
@@ -272,6 +271,49 @@ static NSString *const kHostAddress = @"127.0.0.1";
XCTAssertEqualObjects(foundTXTRecords, @{@"key" : @"value"});
}
- (void)testStartDiscoveryForServiceTypeNilName API_AVAILABLE(ios(13.0)) {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
XCTestExpectation *serviceFoundExpectation = [self expectationWithDescription:@"Service found"];
serviceFoundExpectation.inverted = YES;
NSError *error = nil;
BOOL result = [framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
[serviceFoundExpectation fulfill];
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
}
includePeerToPeer:NO
error:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
// Simulate a service being found with nil name.
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
fakeBrowseResult.txtRecord = @{@"key" : @"value"};
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_added;
nw_endpoint_t fakeEndpoint =
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
fakeBrowseResult.endpointFromResultResult = fakeEndpoint;
fakeBrowseResult.returnNilServiceName = YES;
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
if (fakeBrowser.browseResultsChangedHandler) {
GNCFakeNWBrowseResult *oldFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)oldFakeBrowseResult,
(nw_browse_result_t)fakeBrowseResult, true);
}
[self waitForExpectations:@[ serviceFoundExpectation ] timeout:0.1];
}
- (void)testStartDiscoveryForServiceTypeDuplicate API_AVAILABLE(ios(13.0)) {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
@@ -449,12 +491,13 @@ static NSString *const kHostAddress = @"127.0.0.1";
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
__block BOOL serviceFound = NO;
XCTestExpectation *serviceFoundExpectation = [self expectationWithDescription:@"Service found"];
serviceFoundExpectation.inverted = YES;
NSError *error = nil;
[framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
serviceFound = YES;
[serviceFoundExpectation fulfill];
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
@@ -478,14 +521,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
(nw_browse_result_t)fakeBrowseResult, true);
}
// Allow async blocks to run.
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[delay fulfill];
});
[self waitForExpectations:@[ delay ] timeout:0.5];
XCTAssertFalse(serviceFound);
[self waitForExpectations:@[ serviceFoundExpectation ] timeout:0.1];
}
- (void)testStartDiscoveryIgnoresLoopbackRemove API_AVAILABLE(ios(13.0)) {
@@ -495,7 +531,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
__block BOOL serviceLost = NO;
XCTestExpectation *serviceLostExpectation = [self expectationWithDescription:@"Service lost"];
serviceLostExpectation.inverted = YES;
NSError *error = nil;
[framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
@@ -503,7 +540,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
serviceLost = YES;
[serviceLostExpectation fulfill];
}
includePeerToPeer:NO
error:&error];
@@ -524,14 +561,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
(nw_browse_result_t)newFakeBrowseResult, true);
}
// Allow async blocks to run.
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[delay fulfill];
});
[self waitForExpectations:@[ delay ] timeout:0.5];
XCTAssertFalse(serviceLost);
[self waitForExpectations:@[ serviceLostExpectation ] timeout:0.1];
}
- (void)testStopDiscoveryForServiceType API_AVAILABLE(ios(13.0)) {
@@ -722,8 +752,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
GNCNWFrameworkSocket *socket = [framework connectToHost:address
port:kPort
includePeerToPeer:NO
cancelSource:nil
queue:nil
cancelSource:nil
queue:nil
error:&error];
XCTAssertNotNil(socket);
@@ -742,8 +772,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
GNCNWFrameworkSocket *socket = [framework connectToHost:address
port:kPort
includePeerToPeer:NO
cancelSource:nil
queue:nil
cancelSource:nil
queue:nil
error:&error];
XCTAssertNil(socket);
@@ -208,7 +208,7 @@ static const char *const kTestServiceID = "TestServiceID";
}
- (void)testStartMultipleServicesScanning_Success {
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0, 0)};
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB)};
nearby::api::ble::TxPowerLevel tx_power_level = nearby::api::ble::TxPowerLevel::kUltraLow;
bool result = _medium->StartMultipleServicesScanning(service_uuids, tx_power_level, {});
@@ -217,7 +217,7 @@ static const char *const kTestServiceID = "TestServiceID";
}
- (void)testStartMultipleServicesScanning_Failure {
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0, 0)};
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB)};
nearby::api::ble::TxPowerLevel tx_power_level = nearby::api::ble::TxPowerLevel::kUltraLow;
_fakeGNCBLEMedium.startScanningError = [NSError errorWithDomain:@"test" code:0 userInfo:nil];
@@ -300,8 +300,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -326,8 +326,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -385,8 +385,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -413,8 +413,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -450,8 +450,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -483,8 +483,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -650,6 +650,239 @@ static const char *const kTestServiceID = "TestServiceID";
XCTAssertEqual(server_socket.get(), nullptr);
}
- (void)testOpenServerSocket_Cleanup_InitialState {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
__block BOOL added = NO;
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
added = YES;
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
// Open the server socket.
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
XCTAssertTrue(added);
XCTAssertFalse(removed);
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_AcceptConnection {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
XCTAssertNotNil(capturedHandler);
// Simulate connection accepted -> client socket is created.
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
BOOL result = capturedHandler((GNSSocket *)fakeSocket);
XCTAssertTrue(result);
XCTAssertFalse(removed); // Accepting socket shouldn't remove service manager.
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_CloseClientSocket {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
capturedHandler((GNSSocket *)fakeSocket);
[fakeSocket simulateSocketDidConnect];
__block std::unique_ptr<nearby::api::ble::BleSocket> client_socket = nullptr;
XCTestExpectation *acceptExpectation = [self expectationWithDescription:@"Accept connection"];
nearby::api::ble::BleServerSocket *raw_server_socket = server_socket.get();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
client_socket = raw_server_socket->Accept();
[acceptExpectation fulfill];
});
[self waitForExpectations:@[ acceptExpectation ] timeout:1.0];
XCTAssertTrue(client_socket != nullptr);
// Close the client socket.
client_socket->Close();
// Wait a bit for any async callbacks
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Wait after client close"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[expectation2 fulfill];
});
[self waitForExpectations:@[ expectation2 ] timeout:1.0];
XCTAssertFalse(removed); // Closing client socket shouldn't remove service manager!
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_CloseServerSocket {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
capturedHandler((GNSSocket *)fakeSocket);
[fakeSocket simulateSocketDidConnect];
__block std::unique_ptr<nearby::api::ble::BleSocket> client_socket = nullptr;
XCTestExpectation *acceptExpectation = [self expectationWithDescription:@"Accept connection"];
nearby::api::ble::BleServerSocket *raw_server_socket = server_socket.get();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
client_socket = raw_server_socket->Accept();
[acceptExpectation fulfill];
});
[self waitForExpectations:@[ acceptExpectation ] timeout:1.0];
// Close the server socket.
server_socket->Close();
// Wait a bit for any async callbacks
XCTestExpectation *expectation3 = [self expectationWithDescription:@"Wait after server close"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[expectation3 fulfill];
});
[self waitForExpectations:@[ expectation3 ] timeout:1.0];
XCTAssertTrue(removed); // Closing server socket MUST remove service manager!
}
#pragma mark - Other Tests
- (void)testIsExtendedAdvertisementsAvailable {
@@ -669,8 +902,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -717,8 +950,8 @@ static const char *const kTestServiceID = "TestServiceID";
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
@@ -742,8 +975,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation1 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback1));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback1));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData1);
}
@@ -766,8 +999,8 @@ static const char *const kTestServiceID = "TestServiceID";
serviceData2[[CBUUID UUIDWithString:kTestServiceUUIDString]]);
[expectation2 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback2));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback2));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData2);
@@ -799,8 +1032,8 @@ static const char *const kTestServiceID = "TestServiceID";
[expectation2 fulfill];
}
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -826,8 +1059,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation1 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback1));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback1));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -843,8 +1076,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation2 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback2));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback2));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
@@ -157,4 +157,30 @@ static NSString *const kServiceType = @"_test._tcp";
[self waitForExpectationsWithTimeout:1.0 handler:nil];
}
- (void)testStartDiscoveryCallbacksWithNilName {
XCTestExpectation *foundExpectation = [self expectationWithDescription:@"Service found callback"];
XCTestExpectation *lostExpectation = [self expectationWithDescription:@"Service lost callback"];
nearby::apple::network_utils::NetworkDiscoveredServiceCallback callback;
callback.network_service_discovered_cb = [&](const nearby::NsdServiceInfo &service_info) {
XCTAssertEqual(service_info.GetServiceName(), std::string(""));
XCTAssertEqual(service_info.GetServiceType(), kServiceType.UTF8String);
[foundExpectation fulfill];
};
callback.network_service_lost_cb = [&](const nearby::NsdServiceInfo &service_info) {
XCTAssertEqual(service_info.GetServiceName(), std::string(""));
XCTAssertEqual(service_info.GetServiceType(), kServiceType.UTF8String);
[lostExpectation fulfill];
};
BOOL result = nearby::apple::network_utils::StartDiscovery(
_fakeNWFramework, kServiceType.UTF8String, std::move(callback), YES);
XCTAssertTrue(result);
[_fakeNWFramework triggerServiceFound:nil txtRecords:@{}];
[_fakeNWFramework triggerServiceLost:nil txtRecords:@{}];
[self waitForExpectationsWithTimeout:1.0 handler:nil];
}
@end
@@ -247,6 +247,7 @@ class BleMedium : public api::ble::BleMedium {
GNSPeripheralManager *socketPeripheralManager_;
absl::Mutex scanning_mutex_;
std::vector<Uuid> scanning_service_uuids_ ABSL_GUARDED_BY(scanning_mutex_);
GNSCentralManager *socketCentralManager_ ABSL_GUARDED_BY(scanning_mutex_);
// Used for the blocking version of StartAdvertising and only has an advertisement found callback.
@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/algorithm/container.h"
#import "internal/platform/implementation/apple/ble_medium.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
@@ -157,8 +159,17 @@ void BleMedium::HandleAdvertisementFound(id<GNCPeripheral> peripheral,
return;
}
std::vector<Uuid> scanning_uuids;
{
absl::MutexLock lock(&scanning_mutex_);
scanning_uuids = scanning_service_uuids_;
}
for (CBUUID *key in serviceData.allKeys) {
data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]);
Uuid cpp_uuid = CPPUUIDFromObjC(key);
if (absl::c_find(scanning_uuids, cpp_uuid) == scanning_uuids.end()) {
continue;
}
data.service_data[cpp_uuid] = ByteArrayFromNSData(serviceData[key]);
}
// Add the peripheral to the map if we haven't discovered it yet.
@@ -197,6 +208,7 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
{
absl::MutexLock lock(&scanning_mutex_);
scanning_cb_ = std::make_shared<api::ble::BleMedium::ScanningCallback>(std::move(callback));
scanning_service_uuids_ = {service_uuid};
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUID);
@@ -273,6 +285,7 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel t
{
absl::MutexLock lock(&scanning_mutex_);
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
scanning_service_uuids_ = {service_uuid};
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUID);
@@ -331,6 +344,7 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector<Uuid> &service_u
{
absl::MutexLock lock(&scanning_mutex_);
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
scanning_service_uuids_ = service_uuids;
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUIDs[0]);
@@ -379,6 +393,7 @@ bool BleMedium::StopScanning() {
[socketCentralManager_ stopNoScanMode];
scan_cb_ = nullptr;
scanning_cb_ = nullptr;
scanning_service_uuids_.clear();
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
@@ -534,6 +549,13 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketWithDeadlo
server_socket->SetCloseNotifier([this]() {
absl::MutexLock lock(server_socket_mutex_);
server_socket_ptr_ = nullptr;
if (socketPeripheralManager_ != nil && socketPeripheralServiceManager_ != nil) {
[socketPeripheralManager_
removePeripheralServiceManagerForServiceUUID:socketPeripheralServiceManager_.serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"Weave service removed from peripheral manager.");
}];
}
});
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
@@ -549,15 +571,6 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketWithDeadlo
callbackQueue:connection_callback_queue_];
auto socket_wrapper = std::make_unique<BleSocket>(connection);
socket_wrapper->SetCloseNotifier(
[socketPeripheralManager = socketPeripheralManager_,
serviceUUID = socketPeripheralServiceManager_.serviceUUID]() {
[socketPeripheralManager
removePeripheralServiceManagerForServiceUUID:serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"BleSocket is removed peripheral manager.");
}];
});
connection.connectionHandlers = socket_wrapper->GetInputStream().GetConnectionHandlers();
@@ -612,6 +625,16 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketLegacy(
// Raw pointer for closure capture in the legacy path (risks use-after-free).
BleServerSocket *server_socket_ptr = server_socket.get();
server_socket->SetCloseNotifier([this]() {
if (socketPeripheralManager_ != nil && socketPeripheralServiceManager_ != nil) {
[socketPeripheralManager_
removePeripheralServiceManagerForServiceUUID:socketPeripheralServiceManager_.serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"Weave service removed from peripheral manager.");
}];
}
});
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID]
addPairingCharacteristic:NO
@@ -627,14 +650,6 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketLegacy(
callbackQueue:connection_callback_queue_];
auto socket = std::make_unique<BleSocket>(connection);
socket->SetCloseNotifier([socketPeripheralManager = socketPeripheralManager_,
serviceUUID = socketPeripheralServiceManager_.serviceUUID]() {
[socketPeripheralManager
removePeripheralServiceManagerForServiceUUID:serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"BleSocket is removed peripheral manager.");
}];
});
connection.connectionHandlers = socket->GetInputStream().GetConnectionHandlers();
if (server_socket_ptr) {
@@ -63,7 +63,7 @@ bool StartDiscovery(GNCNWFramework* medium, const std::string& service_type,
serviceFoundHandler:^(NSString* name, NSDictionary<NSString*, NSString*>* txtRecords) {
NsdServiceInfo nsd_service_info;
nsd_service_info.SetServiceType([serviceType UTF8String]);
nsd_service_info.SetServiceName([name UTF8String]);
nsd_service_info.SetServiceName(name ? [name UTF8String] : "");
[txtRecords
enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info](
NSString* key, NSString* val, BOOL* stop) {
@@ -74,7 +74,7 @@ bool StartDiscovery(GNCNWFramework* medium, const std::string& service_type,
serviceLostHandler:^(NSString* name, NSDictionary<NSString*, NSString*>* txtRecords) {
NsdServiceInfo nsd_service_info;
nsd_service_info.SetServiceType([serviceType UTF8String]);
nsd_service_info.SetServiceName([name UTF8String]);
nsd_service_info.SetServiceName(name ? [name UTF8String] : "");
[txtRecords
enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info](
NSString* key, NSString* val, BOOL* stop) {
@@ -51,8 +51,12 @@ namespace api {
std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_folder,
const std::string& file_name) {
// Collapse any path escaping characters.
NSString* parentFolder = [@(parent_folder.c_str()) stringByReplacingOccurrencesOfString:@"../"
withString:@""];
NSString* parentFolderRaw = @(parent_folder.c_str());
if (parentFolderRaw == nil) {
return std::string();
}
NSString* parentFolder = [parentFolderRaw stringByReplacingOccurrencesOfString:@"../"
withString:@""];
NSURL* parentFolderURL = [NSURL fileURLWithPath:parentFolder];
// The only reserved character in a file name on macOS is the forward-slash. It's unclear if iOS
@@ -66,8 +70,12 @@ std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_
// """
//
// See: https://en.wikipedia.org/wiki/Filename
NSString* fileName = [@(file_name.c_str()) stringByReplacingOccurrencesOfString:@"/"
withString:@":"];
NSString* fileNameRaw = @(file_name.c_str());
if (fileNameRaw == nil) {
return std::string();
}
NSString* fileName = [fileNameRaw stringByReplacingOccurrencesOfString:@"/"
withString:@":"];
NSString* baseName = [fileName stringByDeletingPathExtension];
NSString* extension = [fileName pathExtension];
@@ -86,8 +94,12 @@ std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_
std::string ImplementationPlatform::GetDownloadPath(const std::string& parent_folder,
const std::string& file_name) {
NSString* parentFolderRaw = @(parent_folder.c_str());
if (parentFolderRaw == nil) {
return std::string();
}
NSString* customSavePath =
[NSTemporaryDirectory() stringByAppendingPathComponent:@(parent_folder.c_str())];
[NSTemporaryDirectory() stringByAppendingPathComponent:parentFolderRaw];
return GetCustomSavePath(customSavePath.UTF8String, file_name);
}
@@ -21,6 +21,7 @@
#include <utility>
#include "absl/time/time.h"
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
#include "internal/platform/runnable.h"
// Defines the state of a scheduled task. This enum is at global scope
@@ -93,7 +94,23 @@ class ExecutorCancelable : public nearby::api::Cancelable {
namespace nearby {
namespace apple {
namespace {
void ExecuteRunnable(Runnable &runnable) {
@try {
try {
runnable();
} catch (const std::exception &e) {
GNCLoggerError(@"Runnable threw C++ exception: %s", e.what());
} catch (...) {
GNCLoggerError(@"Runnable threw unknown C++ exception");
}
} @catch (NSException *e) {
GNCLoggerError(@"Runnable threw ObjC exception: %@: %@", e.name, e.reason);
}
}
} // namespace
ScheduledExecutor::ScheduledExecutor() { impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:1]; }
@@ -106,7 +123,6 @@ ScheduledExecutor::~ScheduledExecutor() {
impl_ = nil;
}
std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable,
absl::Duration duration) {
if (impl_.shuttingDown) return std::shared_ptr<api::Cancelable>(nullptr);
@@ -133,7 +149,7 @@ std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable
}
GNCScheduledTaskState expected = GNCScheduledTaskState::kScheduled;
if (task->_state.compare_exchange_strong(expected, GNCScheduledTaskState::kRunning)) {
task->_runnable();
ExecuteRunnable(task->_runnable);
task->_state.store(GNCScheduledTaskState::kDone);
}
}];
@@ -152,7 +168,7 @@ bool ScheduledExecutor::DoSubmit(Runnable &&runnable) {
// Submit the runnable to the queue.
__block Runnable local_runnable = std::move(runnable);
[impl_.queue addOperationWithBlock:^{
local_runnable();
ExecuteRunnable(local_runnable);
}];
return true;
}
@@ -19,7 +19,6 @@
#include <optional>
#include <string>
#include "absl/base/attributes.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "internal/platform/cancellation_flag.h"
@@ -29,8 +28,7 @@
#include "internal/platform/mac_address.h"
#include "internal/platform/output_stream.h"
namespace nearby {
namespace api {
namespace nearby::api {
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
class BluetoothDevice {
@@ -213,29 +211,6 @@ class BluetoothClassicMedium {
DefaultCallback<BluetoothDevice&>();
};
class Observer {
public:
virtual ~Observer() = default;
// Called when a new `device` is added to the adapter.
virtual void DeviceAdded(BluetoothDevice& device) {}
// Called when `device` is removed from the adapter.
virtual void DeviceRemoved(BluetoothDevice& device) {}
// Called when the address of `device` changed due to pairing.
virtual void DeviceAddressChanged(BluetoothDevice& device,
absl::string_view old_address) {}
// Called when the paired property of `device` changed.
virtual void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) {}
// Called when `device` has connected or disconnected.
virtual void DeviceConnectedStateChanged(BluetoothDevice& device,
bool connected) {}
};
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
//
// Returns true once the process of discovery has been initiated.
@@ -273,7 +248,7 @@ class BluetoothClassicMedium {
// UUID.
//
// Returns nullptr error.
virtual std::unique_ptr<BluetoothServerSocket> ListenForService(
virtual std::shared_ptr<BluetoothServerSocket> ListenForService(
const std::string& service_name, const std::string& service_uuid) = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()
@@ -285,12 +260,8 @@ class BluetoothClassicMedium {
BluetoothDevice& remote_device) = 0;
virtual BluetoothDevice* GetRemoteDevice(MacAddress mac_address) = 0;
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
};
} // namespace api
} // namespace nearby
} // namespace nearby::api
#endif // PLATFORM_API_BLUETOOTH_CLASSIC_H_
@@ -16,6 +16,7 @@
#define PLATFORM_API_DEVICE_INFO_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
@@ -39,6 +40,7 @@ class DeviceInfo {
kWindows,
kMacOS
};
enum class SuspendResumeEvent { kSuspend, kResume };
virtual ~DeviceInfo() = default;
@@ -69,6 +71,14 @@ class DeviceInfo {
// Control device sleep
virtual bool PreventSleep() = 0;
virtual bool AllowSleep() = 0;
// Monitor suspend/resume events.
// Returns a listener id that can be used to unregister the listener.
virtual int64_t RegisterSuspendResumeListener(
std::function<void(SuspendResumeEvent)> callback) {
return 0;
}
virtual void UnregisterSuspendResumeListener(int64_t listener_id) {}
};
template <typename Sink>
+19 -4
View File
@@ -59,10 +59,11 @@ cc_library(
"//internal/platform/implementation:types",
"//internal/platform/implementation/shared:count_down_latch",
"//internal/platform/implementation/shared:posix_mutex",
"//internal/test",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
@@ -151,13 +152,13 @@ cc_test(
"//internal/platform:test_util",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//third_party/gloop/thread/fiber",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
"@com_google_nisaba//nisaba/port:thread_pool/fiber",
],
)
@@ -187,13 +188,11 @@ cc_library(
visibility = [
"//connections:__subpackages__",
"//connections:partners",
"//internal/account:__subpackages__",
"//internal/auth:__subpackages__",
"//internal/crypto:__subpackages__",
"//internal/data:__subpackages__",
"//internal/network:__subpackages__",
"//internal/platform:__subpackages__",
"//internal/preferences:__subpackages__",
"//internal/proto/analytics:__subpackages__",
"//internal/weave:__subpackages__",
"//location/nearby/sharing/sdk:__subpackages__",
@@ -217,6 +216,7 @@ cc_library(
"//internal/platform/implementation/shared:file",
"//third_party/gloop/thread",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:no_destructor",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
@@ -237,3 +237,18 @@ cc_library(
"@nlohmann_json//:json",
],
)
cc_test(
name = "scheduled_executor_test",
srcs = ["scheduled_executor_test.cc"],
deps = [
":g3",
":types",
"//internal/platform:base",
"//internal/platform:test_util",
"//internal/platform:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
)
@@ -19,11 +19,11 @@
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "third_party/gloop/thread/fiber/fiber.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/awdl.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/nsd_service_info.h"
#include "thread/fiber/fiber.h"
namespace nearby {
namespace g3 {
@@ -240,10 +240,10 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
return socket;
}
std::unique_ptr<api::BluetoothServerSocket>
std::shared_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
auto socket = std::make_unique<BluetoothServerSocket>(GetAdapter());
auto socket = std::make_shared<BluetoothServerSocket>(GetAdapter());
socket->SetCloseNotifier([this, uuid = service_uuid]() {
absl::MutexLock lock(mutex_);
sockets_.erase(uuid);
@@ -264,15 +264,5 @@ api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
return MediumEnvironment::Instance().FindBluetoothDevice(mac_address);
}
void BluetoothClassicMedium::AddObserver(
api::BluetoothClassicMedium::Observer* observer) {
MediumEnvironment::Instance().AddObserver(observer);
}
void BluetoothClassicMedium::RemoveObserver(
api::BluetoothClassicMedium::Observer* observer) {
MediumEnvironment::Instance().RemoveObserver(observer);
}
} // namespace g3
} // namespace nearby
@@ -195,7 +195,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// UUID.
//
// Returns nullptr on error.
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
std::shared_ptr<api::BluetoothServerSocket> ListenForService(
const std::string& service_name, const std::string& service_uuid) override
ABSL_LOCKS_EXCLUDED(mutex_);
@@ -206,9 +206,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
api::BluetoothDevice* GetRemoteDevice(MacAddress mac_address) override;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
private:
absl::Mutex mutex_;
BluetoothAdapter* adapter_; // Our device adapter; read-only.
@@ -20,6 +20,7 @@
#include <string>
#include "absl/base/attributes.h"
#include "absl/base/no_destructor.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
@@ -239,7 +240,8 @@ ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
}
std::unique_ptr<Timer> ImplementationPlatform::CreateTimer() {
return std::make_unique<g3::Timer>();
static absl::NoDestructor<g3::ScheduledExecutor> timer_executor;
return std::make_unique<g3::Timer>(timer_executor.get());
}
std::unique_ptr<nearby::api::DeviceInfo>
@@ -0,0 +1,133 @@
// 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 "internal/platform/scheduled_executor.h"
#include <atomic>
#include "gtest/gtest.h"
#include "absl/time/time.h"
#include "internal/platform/cancelable.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/medium_environment.h"
namespace nearby {
// kShortDelay must be significant enough to guarantee that OS under heavy load
// should be able to execute the non-blocking test paths within this time.
absl::Duration kShortDelay = absl::Milliseconds(200);
// kLongDelay must be long enough to make sure that under OS under heavy load
// will let kShortDelay fire and jobs scheduled before the kLongDelay fires.
absl::Duration kLongDelay = 10 * kShortDelay;
TEST(ScheduledExecutorTest, SimulatedClockCanSchedule) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
CountDownLatch first_task_latch(1);
CountDownLatch second_task_latch(1);
// schedule job due in kLongDelay.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 1);
value = 5;
first_task_latch.CountDown();
},
kLongDelay);
// schedule job due in kShortDelay; must fire before the first one.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 0);
value = 1;
second_task_latch.CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
second_task_latch.Await();
EXPECT_EQ(value, 1);
MediumEnvironment::Instance().FastForward(kLongDelay - kShortDelay);
first_task_latch.Await();
EXPECT_EQ(value, 5);
// Very long sleep to make sure that the sleep is truly simulated.
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
TEST(ScheduledExecutorTest,
DestroyExecutorWithSimulatedClockIgnoresPendingTasks) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
{
ScheduledExecutor executor;
executor.Schedule(
[&]() {
// This task should never be executed.
EXPECT_TRUE(false);
},
kShortDelay);
}
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
std::atomic_int i = 0;
CountDownLatch latch[] = {CountDownLatch(1), CountDownLatch(1)};
Cancelable cancelable = executor.ScheduleRepeatedly(
[&]() {
value++;
latch[i.fetch_add(1)].CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
// Advance to just before the first execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
// Advance past the first execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[0].Await(absl::Seconds(1));
EXPECT_EQ(value, 1);
// Advance to just before the second execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 1);
// Advance past the second execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[1].Await(absl::Seconds(1));
EXPECT_EQ(value, 2);
// Cancel the task.
cancelable.Cancel();
// Advance a long time and make sure it doesn't run again.
MediumEnvironment::Instance().FastForward(kLongDelay * 5);
EXPECT_EQ(value, 2);
MediumEnvironment::Instance().Stop();
}
} // namespace nearby
+15 -6
View File
@@ -19,10 +19,13 @@
#include <memory>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/g3/scheduled_executor.h"
#include "internal/platform/implementation/cancelable.h"
#include "internal/platform/implementation/scheduled_executor.h"
#include "internal/platform/implementation/timer.h"
namespace nearby {
@@ -30,8 +33,11 @@ namespace g3 {
class Timer : public api::Timer {
public:
Timer() = default;
~Timer() override = default;
explicit Timer(api::ScheduledExecutor* absl_nonnull executor)
: executor_(executor) {};
~Timer() override {
Stop();
};
bool Create(int delay, int interval,
absl::AnyInvocable<void()> callback) override {
@@ -52,13 +58,16 @@ class Timer : public api::Timer {
task_.reset();
return result;
}
return false;
return true;
}
private:
bool Schedule(absl::Duration delay) {
absl::MutexLock lock(mutex_);
task_ = executor_.Schedule([this]() { TriggerCallback(); }, delay);
if (is_stopped_) {
return false;
}
task_ = executor_->Schedule([this]() { TriggerCallback(); }, delay);
return true;
}
@@ -77,7 +86,7 @@ class Timer : public api::Timer {
std::atomic_bool is_stopped_;
absl::Duration interval_;
std::shared_ptr<api::Cancelable> task_ ABSL_GUARDED_BY(mutex_);
ScheduledExecutor executor_;
api::ScheduledExecutor* absl_nonnull const executor_;
};
} // namespace g3
@@ -15,7 +15,6 @@
#ifndef PLATFORM_API_SCHEDULED_EXECUTOR_H_
#define PLATFORM_API_SCHEDULED_EXECUTOR_H_
#include <cstdint>
#include <memory>
#include "absl/time/time.h"
@@ -72,7 +72,13 @@ cc_library(
"timer.h",
"utils.h",
],
defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
defines = [
"_SILENCE_CLANG_COROUTINE_MESSAGE",
"_WIN32_WINNT=_WIN32_WINNT_WIN10",
],
linkopts = [
"powrprof.lib",
],
tags = ["windows"],
visibility = ["//visibility:private"],
deps = [
@@ -32,6 +32,7 @@
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
#include "internal/platform/implementation/windows/bluetooth_classic_device.h"
#include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h"
#include "internal/platform/implementation/windows/bluetooth_classic_socket.h"
#include "internal/platform/implementation/windows/bluetooth_pairing.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h"
@@ -43,8 +44,7 @@
#include "internal/platform/logging.h"
#include "internal/platform/mac_address.h"
namespace nearby {
namespace windows {
namespace nearby::windows {
namespace {
using ::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService;
using ::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId;
@@ -124,7 +124,13 @@ BluetoothClassicMedium::BluetoothClassicMedium(
&BluetoothClassicMedium::OnScanModeChanged, this, std::placeholders::_1));
}
BluetoothClassicMedium::~BluetoothClassicMedium() {}
BluetoothClassicMedium::~BluetoothClassicMedium() {
// Clear the close notifier to prevent UAF if the server_socket_ outlives
// the BluetoothClassicMedium.
if (raw_server_socket_ != nullptr) {
raw_server_socket_->SetCloseNotifier(nullptr);
}
}
bool BluetoothClassicMedium::StartDiscovery(
BluetoothClassicMedium::DiscoveryCallback discovery_callback) {
@@ -259,7 +265,7 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
// UUID.
//
// Returns nullptr error.
std::unique_ptr<api::BluetoothServerSocket>
std::shared_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
VLOG(1) << "ListenForService is called with service name: " << service_name
@@ -283,14 +289,24 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name,
bool radio_discoverable =
scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable;
bool result = StartAdvertising(radio_discoverable);
if (rfcomm_provider_ != nullptr &&
is_radio_discoverable_ == radio_discoverable) {
LOG(WARNING) << __func__
<< ": Ignore StartAdvertising due to no change to "
"current advertising.";
return server_socket_;
}
if (!result) {
auto server_socket = StartAdvertising(radio_discoverable);
if (!server_socket) {
LOG(ERROR) << __func__ << ": Failed to start listening.";
return nullptr;
}
return std::move(server_socket_);
raw_server_socket_ = server_socket.get();
server_socket_ = std::move(server_socket);
return server_socket_;
}
api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
@@ -685,9 +701,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added(
if (discovery_callback_.device_discovered_cb != nullptr) {
discovery_callback_.device_discovered_cb(*device);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceAdded(*device);
}
return winrt::fire_and_forget();
}
@@ -753,9 +766,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated(
LOG(INFO) << __func__
<< ": Notifying device paired changed: " << std::boolalpha
<< new_paired_status;
for (auto& observer : observers_.GetObservers()) {
observer->DevicePairedChanged(*device, new_paired_status);
}
}
return winrt::fire_and_forget();
@@ -803,10 +813,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed(
discovery_callback_.device_lost_cb(*device);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceRemoved(*device);
}
RemoveRemoteDevice(mac_address);
return winrt::fire_and_forget();
@@ -833,25 +839,19 @@ bool BluetoothClassicMedium::IsWatcherRunning() {
(status == DeviceWatcherStatus::Stopping);
}
bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
std::shared_ptr<BluetoothServerSocket>
BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
LOG(INFO) << __func__
<< ": StartAdvertising is called with radio_discoverable: "
<< radio_discoverable << ".";
std::shared_ptr<BluetoothServerSocket> server_socket;
try {
if (rfcomm_provider_ != nullptr &&
is_radio_discoverable_ == radio_discoverable) {
LOG(WARNING) << __func__
<< ": Ignore StartAdvertising due to no change to "
"current advertising.";
return true;
}
if (rfcomm_provider_ != nullptr && !StopAdvertising()) {
LOG(WARNING) << __func__
<< ": Failed to StartAdvertising due to cannot stop "
"running advertising.";
return false;
return nullptr;
}
rfcomm_provider_ =
@@ -859,79 +859,57 @@ bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
RfcommServiceId::FromUuid(winrt::guid(service_uuid_)))
.get();
server_socket_ = std::make_unique<BluetoothServerSocket>(
server_socket = BluetoothServerSocket::Create(
winrt::to_string(rfcomm_provider_.ServiceId().AsString()));
raw_server_socket_ = server_socket_.get();
if (!server_socket_->listen()) {
if (!server_socket->listen()) {
LOG(ERROR) << __func__
<< ": Failed to StartAdvertising due to cannot start socket.";
server_socket_->Close();
server_socket_ = nullptr;
rfcomm_provider_ = nullptr;
return false;
return nullptr;
}
server_socket_->SetCloseNotifier([&]() { StopAdvertising(); });
server_socket->SetCloseNotifier([&]() { StopAdvertising(); });
// Set the SDP attributes and start Bluetooth advertising
InitializeServiceSdpAttributes(rfcomm_provider_, service_name_);
// Start to advertising.
rfcomm_provider_.StartAdvertising(server_socket_->stream_socket_listener(),
rfcomm_provider_.StartAdvertising(server_socket->stream_socket_listener(),
radio_discoverable);
is_radio_discoverable_ = radio_discoverable;
LOG(INFO) << ": StartListening completed successfully.";
return true;
return server_socket;
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
LOG(ERROR) << __func__
<< ": Exception setting up for listen: " << exception.what();
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__ << ": Exception setting up for listen: " << ex.code()
<< ": " << winrt::to_string(ex.message());
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception.";
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
}
}
bool BluetoothClassicMedium::StopAdvertising() {
VLOG(1) << __func__ << ": StopAdvertising is called";
bool result = false;
try {
if (rfcomm_provider_ == nullptr) {
LOG(ERROR) << __func__
@@ -940,12 +918,9 @@ bool BluetoothClassicMedium::StopAdvertising() {
}
rfcomm_provider_.StopAdvertising();
rfcomm_provider_ = nullptr;
raw_server_socket_ = nullptr;
server_socket_ = nullptr;
LOG(INFO) << ": StopAdvertising completed successfully.";
return true;
result = true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": StopAdvertising exception: " << exception.what();
@@ -957,9 +932,9 @@ bool BluetoothClassicMedium::StopAdvertising() {
}
rfcomm_provider_ = nullptr;
raw_server_socket_ = nullptr;
server_socket_ = nullptr;
return false;
raw_server_socket_ = nullptr;
return result;
}
bool BluetoothClassicMedium::InitializeServiceSdpAttributes(
@@ -988,5 +963,4 @@ bool BluetoothClassicMedium::InitializeServiceSdpAttributes(
}
}
} // namespace windows
} // namespace nearby
} // namespace nearby::windows

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