Implement Dynamic Role Switch

Implement dynamic role switch in bwu manager. When NC receive BWU request, it will check if it should switch role based on the device's capability.

PiperOrigin-RevId: 755703443
This commit is contained in:
Johnson Lu
2025-05-06 23:38:16 -07:00
committed by Copybara-Service
parent 946e865db2
commit ee9266b175
12 changed files with 278 additions and 17 deletions
+1
View File
@@ -85,6 +85,7 @@ cc_library(
"//sharing:__subpackages__",
],
deps = [
"//connections/implementation/proto:offline_wire_formats_cc_proto",
"//internal/interop:authentication_status",
"//internal/platform:base",
"//internal/platform:types",
+2
View File
@@ -16,6 +16,7 @@
#include <string>
#include <vector>
#include "connections/implementation/proto/offline_wire_formats.pb.h"
#include "connections/options_base.h"
#include "internal/platform/byte_array.h"
#include "proto/connections_enums.pb.h"
@@ -34,6 +35,7 @@ struct ConnectionInfo {
std::vector<location::nearby::proto::connections::Medium> supported_mediums;
std::int32_t keep_alive_interval_millis;
std::int32_t keep_alive_timeout_millis;
std::optional<location::nearby::connections::MediumRole> medium_role;
};
// Connection Options: used for both Advertising and Discovery.
+48 -7
View File
@@ -107,6 +107,7 @@ 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;
@@ -811,6 +812,16 @@ ConnectionInfo BasePcpHandler::FillConnectionInfo(
connection_info.bssid = wifi_info.bssid;
connection_info.ap_frequency = wifi_info.ap_frequency;
connection_info.ip_address = wifi_info.ip_address_4_bytes;
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);
}
NEARBY_LOGS(INFO) << "Query for WIFI information: is_supports_5_ghz="
<< connection_info.supports_5_ghz
<< "; bssid=" << connection_info.bssid
@@ -2021,13 +2032,43 @@ Exception BasePcpHandler::OnIncomingConnection(
connection_info.bssid = medium_metadata.bssid();
connection_info.ap_frequency = medium_metadata.ap_frequency();
connection_info.ip_address = medium_metadata.ip_address();
NEARBY_LOGS(INFO) << connection_request.endpoint_id()
<< "'s WIFI information: is_supports_5_ghz="
<< connection_info.supports_5_ghz
<< "; bssid=" << connection_info.bssid
<< "; ap_frequency=" << connection_info.ap_frequency
<< "Mhz; ip_address in bytes format="
<< absl::BytesToHexString(connection_info.ip_address);
if (medium_metadata.has_medium_role()) {
connection_info.medium_role.emplace(medium_metadata.medium_role());
}
if (medium_metadata.has_medium_role()) {
NEARBY_LOGS(INFO)
<< connection_request.endpoint_id()
<< "'s WIFI information: is_supports_5_ghz="
<< connection_info.supports_5_ghz << "; bssid=" << connection_info.bssid
<< "; ap_frequency=" << connection_info.ap_frequency
<< "Mhz; ip_address in bytes format="
<< absl::BytesToHexString(connection_info.ip_address)
<< "; support_wifi_direct_group_owner="
<< medium_metadata.medium_role().support_wifi_direct_group_owner()
<< "; support_wifi_direct_group_client="
<< medium_metadata.medium_role().support_wifi_direct_group_client()
<< "; support_wifi_hotspot_host="
<< medium_metadata.medium_role().support_wifi_hotspot_host()
<< "; support_wifi_hotspot_client="
<< medium_metadata.medium_role().support_wifi_hotspot_client()
<< "; support_wifi_aware_publisher="
<< medium_metadata.medium_role().support_wifi_aware_publisher()
<< "; support_wifi_aware_subscriber="
<< medium_metadata.medium_role().support_wifi_aware_subscriber()
<< "; support_awdl_publisher="
<< medium_metadata.medium_role().support_awdl_publisher()
<< "; support_awdl_subscriber="
<< medium_metadata.medium_role().support_awdl_subscriber();
} else {
NEARBY_LOGS(INFO) << connection_request.endpoint_id()
<< "'s WIFI information: is_supports_5_ghz="
<< connection_info.supports_5_ghz
<< "; bssid=" << connection_info.bssid
<< "; ap_frequency=" << connection_info.ap_frequency
<< "Mhz; ip_address in bytes format="
<< absl::BytesToHexString(connection_info.ip_address)
<< "; has no mediumRole";
}
// We've successfully connected to the device, and are now about to jump on to
// the EncryptionRunner thread to start running our encryption protocol. We'll
+69 -1
View File
@@ -61,7 +61,9 @@ namespace connections {
namespace {
using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame;
using ::location::nearby::connections::MediumRole;
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::V1Frame;
using ::location::nearby::proto::connections::BandwidthUpgradeErrorStage;
using ::location::nearby::proto::connections::BandwidthUpgradeResult;
@@ -292,6 +294,40 @@ void BwuManager::InitiateBwuForEndpoint(ClientProxy* client,
return;
}
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)) {
if (!channel
->Write(parser::ForBwuPathRequest(
client->GetUpgradeMediums(endpoint_id).GetMediums(true),
medium_role))
.Ok()) {
LOG(ERROR) << "BwuManager couldn't complete the upgrade for endpoint "
<< endpoint_id << " to medium "
<< location::nearby::proto::connections::Medium_Name(
proposed_medium)
<< " because it failed to write the "
"BWU_NEGOTIATION.UPGRADE_PATH_REQUEST OfflineFrame.";
client->GetAnalyticsRecorder().OnBandwidthUpgradeError(
endpoint_id, BandwidthUpgradeResult::RESULT_IO_ERROR,
BandwidthUpgradeErrorStage::NETWORK_AVAILABLE,
OperationResultCode::
CONNECTIVITY_GENERIC_WRITING_CHANNEL_IO_ERROR);
return;
}
NEARBY_LOGS(INFO)
<< "BwuManager successfully wrote the "
"BANDWIDTH_UPGRADE_NEGOTIATION.UPGRADE_PATH_REQUEST "
"OfflineFrame while upgrading endpoint "
<< endpoint_id << " to medium "
<< location::nearby::proto::connections::Medium_Name(
proposed_medium);
return;
}
}
std::string service_id = channel->GetServiceId();
ByteArray bytes = handler->InitializeUpgradedMediumForEndpoint(
client, service_id, endpoint_id);
@@ -755,7 +791,20 @@ void BwuManager::ProcessBwuPathAvailableEvent(
return;
}
bool abort_bwu = false;
if (client->IsIncomingConnection(endpoint_id)) {
if (!is_dynamic_role_switch_enabled_) {
abort_bwu = true;
} else {
auto medium_role = client->GetMediumRole(endpoint_id);
if (medium_role.has_value() &&
!NeedToSwitchRole(client, endpoint_id, upgrade_medium,
medium_role.value())) {
abort_bwu = true;
}
}
}
if (abort_bwu) {
NEARBY_LOGS(INFO)
<< "ProcessBandwidthUpgradePathAvailableEvent ignored by Advertiser";
return;
@@ -919,7 +968,7 @@ BwuManager::ProcessBwuPathAvailableEventInternal(
}
bool enable_ble_v2 = NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::kEnableBleV2);
config_package_nearby::nearby_connections_feature::kEnableBleV2);
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableStopBLEScanningOnWifiUpgrade)) {
@@ -1561,6 +1610,25 @@ void BwuManager::AttemptToRecordBandwidthUpgradeErrorForUnknownEndpoint(
"connect to us, so skipping analytics for his error.";
}
bool BwuManager::NeedToSwitchRole(
ClientProxy* client, const std::string& endpoint_id, Medium medium,
const location::nearby::connections::MediumRole& medium_role) {
if (GetLocalOsInfo(client).type() == OsInfo::APPLE) {
switch (medium) {
case Medium::WIFI_HOTSPOT:
return medium_role.support_wifi_hotspot_host();
default:
break;
}
}
return false;
}
const location::nearby::connections::OsInfo& BwuManager::GetLocalOsInfo(
ClientProxy* client) const {
return client->GetLocalOsInfo();
}
absl::Duration BwuManager::CalculateNextRetryDelay(
const std::string& endpoint_id) {
auto item = retry_delays_.find(endpoint_id);
+12
View File
@@ -213,6 +213,13 @@ class BwuManager : public EndpointManager::FrameProcessor {
location::nearby::proto::connections::OperationResultCode
operation_result_code);
bool NeedToSwitchRole(
ClientProxy* client, const std::string& endpoint_id, Medium medium,
const location::nearby::connections::MediumRole& medium_role);
virtual const location::nearby::connections::OsInfo& GetLocalOsInfo(
ClientProxy* client) const;
bool is_single_threaded_for_testing_ = false;
Config config_;
@@ -253,6 +260,11 @@ class BwuManager : public EndpointManager::FrameProcessor {
// retry happen, then we can not find the last delay used in the alarm. Thus
// using a different map to keep track of the delays per endpoint.
absl::flat_hash_map<std::string, absl::Duration> retry_delays_;
// Whether the dynamic role switch feature is enabled.
bool is_dynamic_role_switch_enabled_ = NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch);
};
} // namespace connections
@@ -46,7 +46,9 @@ using ::location::nearby::analytics::proto::ConnectionsLog;
using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame;
using ::location::nearby::connections::
BandwidthUpgradeNegotiationFrame_UpgradePathInfo;
using ::location::nearby::connections::MediumRole;
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::V1Frame;
using ::location::nearby::proto::connections::DisconnectionReason;
@@ -236,6 +238,51 @@ TEST(BwuManagerBaseTest, AllowToUpgradeMedium) {
bwu_manager->Shutdown();
}
TEST(BwuManagerBaseTest, InitiateBwu_NeedToSwitchRole_Success) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
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);
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,
ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION);
bwu_manager->Shutdown();
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
false);
}
class BwuManagerTestParam : public BwuManagerTest,
public ::testing::WithParamInterface<bool> {
protected:
@@ -72,6 +72,7 @@ namespace connections {
namespace {
using ::location::nearby::analytics::proto::ConnectionsLog;
using ::location::nearby::connections::MediumRole;
using ::location::nearby::connections::OsInfo;
constexpr char kEndpointIdChars[] = {
@@ -959,6 +960,11 @@ std::optional<OsInfo> ClientProxy::GetRemoteOsInfo(
return std::nullopt;
}
void ClientProxy::SetLocalOsType(
const location::nearby::connections::OsInfo::OsType& os_type) {
local_os_info_.set_type(os_type);
}
void ClientProxy::SetRemoteOsInfo(absl::string_view endpoint_id,
const OsInfo& remote_os_info) {
ConnectionPair* item = LookupConnection(endpoint_id);
@@ -1345,6 +1351,15 @@ void ClientProxy::UpdateDctDeviceName(absl::string_view device_name) {
}
}
std::optional<MediumRole> ClientProxy::GetMediumRole(
absl::string_view endpoint_id) const {
const ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
return item->first.connection_options.connection_info.medium_role;
}
return std::nullopt;
}
std::optional<std::string> ClientProxy::GetEndpointIdForDct() const {
if (dct_endpoint_id_.empty()) {
return std::nullopt;
+6 -1
View File
@@ -285,9 +285,11 @@ class ClientProxy final {
std::string Dump();
const location::nearby::connections::OsInfo& GetLocalOsInfo() const;
virtual const location::nearby::connections::OsInfo& GetLocalOsInfo() const;
std::optional<location::nearby::connections::OsInfo> GetRemoteOsInfo(
absl::string_view endpoint_id) const;
void SetLocalOsType(
const location::nearby::connections::OsInfo::OsType& os_type);
void SetRemoteOsInfo(
absl::string_view endpoint_id,
const location::nearby::connections::OsInfo& remote_os_info);
@@ -350,6 +352,9 @@ class ClientProxy final {
// Updates the DCT device name before advertising.
void UpdateDctDeviceName(absl::string_view device_name);
std::optional<location::nearby::connections::MediumRole> GetMediumRole(
absl::string_view endpoint_id) const;
/** Bitmask for bt multiplex connection support. */
// Note. Deprecates the first and second bit of BT_MULTIPLEX_ENABLED and
// WIFI_LAN_MULTIPLEX_ENABLED and shift them to the third and the forth bit.
@@ -99,6 +99,9 @@ constexpr auto kEnableMultiplexAwdl =
// Enable/Disable AWDL in Nearby connections SDK.
constexpr auto kEnableAwdl =
flags::Flag<bool>(kConfigPackage, "45690762", false);
// When true, enable dynamic role switch in NC.
constexpr auto kEnableDynamicRoleSwitch =
flags::Flag<bool>(kConfigPackage, "45696452", false);
} // namespace nearby_connections_feature
} // namespace config_package_nearby
} // namespace connections
@@ -43,6 +43,7 @@ using ::location::nearby::connections::ConnectionRequestFrame;
using ::location::nearby::connections::ConnectionResponseFrame;
using ::location::nearby::connections::KeepAliveFrame;
using ::location::nearby::connections::LocationHint;
using ::location::nearby::connections::MediumRole;
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::PayloadTransferFrame;
@@ -110,6 +111,13 @@ ByteArray ForConnectionRequestConnections(
medium_metadata->set_ap_frequency(conection_info.ap_frequency);
if (!conection_info.ip_address.empty())
medium_metadata->set_ip_address(conection_info.ip_address);
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch) &&
conection_info.medium_role.has_value()) {
medium_metadata->mutable_medium_role()->MergeFrom(
conection_info.medium_role.value());
}
if (!conection_info.supported_mediums.empty()) {
for (const auto& medium : conection_info.supported_mediums) {
connection_request->add_mediums(MediumToConnectionRequestMedium(medium));
@@ -483,6 +491,30 @@ ByteArray ForBwuFailure(const UpgradePathInfo& info) {
auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info();
*upgrade_path_info = info;
*sub_frame->mutable_upgrade_path_info() = info;
return ToBytes(std::move(frame));
}
ByteArray ForBwuPathRequest(const std::vector<Medium>& mediums,
const MediumRole& medium_role) {
OfflineFrame frame;
frame.set_version(OfflineFrame::V1);
auto* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION);
auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation();
sub_frame->set_event_type(
BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_REQUEST);
auto* upgrade_path_request =
sub_frame->mutable_upgrade_path_info()->mutable_upgrade_path_request();
for (const auto& medium : mediums) {
upgrade_path_request->add_mediums(MediumToUpgradePathInfoMedium(medium));
}
auto* role =
upgrade_path_request->mutable_medium_meta_data()->mutable_medium_role();
role->MergeFrom(medium_role);
return ToBytes(std::move(frame));
}
@@ -100,6 +100,9 @@ ByteArray ForBwuWebrtcPathAvailable(
const std::string& peer_id,
const location::nearby::connections::LocationHint& location_hint_a);
ByteArray ForBwuFailure(const UpgradePathInfo& info);
ByteArray ForBwuPathRequest(
const std::vector<Medium>& mediums,
const location::nearby::connections::MediumRole& medium_role);
ByteArray ForBwuLastWrite();
ByteArray ForBwuSafeToClose();
@@ -39,6 +39,7 @@ using ::location::nearby::connections::OsInfo;
using ::location::nearby::connections::PayloadTransferFrame;
using ::location::nearby::connections::V1Frame;
using Medium = ::location::nearby::proto::connections::Medium;
using ::location::nearby::connections::MediumRole;
using ::protobuf_matchers::EqualsProto;
constexpr absl::string_view kEndpointId{"ABC"};
@@ -145,6 +146,10 @@ TEST(OfflineFramesTest, CanGenerateLegacyConnectionRequest) {
}
TEST(OfflineFramesTest, CanGenerateConnectionsConnectionRequest) {
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableDynamicRoleSwitch,
true);
constexpr absl::string_view kExpected =
R"pb(
version: V1
@@ -160,6 +165,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionsConnectionRequest) {
bssid: "FF:FF:FF:FF:FF:FF"
ip_address: "8xqT"
ap_frequency: 2412
medium_role: < support_wifi_hotspot_client: true >
>
mediums: MDNS
mediums: BLUETOOTH
@@ -187,6 +193,8 @@ TEST(OfflineFramesTest, CanGenerateConnectionsConnectionRequest) {
location::nearby::connections::CONNECTIONS_ENDPOINT);
connections_device.set_endpoint_info("XYZ");
MediumRole medium_role;
medium_role.set_support_wifi_hotspot_client(true);
ConnectionInfo connection_info{std::string(kEndpointId),
ByteArray{std::string(kEndpointName)},
kNonce,
@@ -197,7 +205,8 @@ TEST(OfflineFramesTest, CanGenerateConnectionsConnectionRequest) {
std::vector<Medium, std::allocator<Medium>>(
kMediums.begin(), kMediums.end()),
kKeepAliveIntervalMillis,
kKeepAliveTimeoutMillis};
kKeepAliveTimeoutMillis,
medium_role};
ByteArray bytes =
ForConnectionRequestConnections(connections_device, connection_info);
auto response = FromBytes(bytes);
@@ -623,10 +632,7 @@ TEST(OfflineFramesTest, CanGenerateAutoReconnectIntroduction) {
version: V1
v1: <
type: AUTO_RECONNECT
auto_reconnect: <
event_type: CLIENT_INTRODUCTION
endpoint_id: "ABC"
>
auto_reconnect: < event_type: CLIENT_INTRODUCTION endpoint_id: "ABC" >
>)pb";
ByteArray bytes = ForAutoReconnectIntroduction(std::string(kEndpointId));
auto response = FromBytes(bytes);
@@ -641,9 +647,7 @@ TEST(OfflineFramesTest, CanGenerateAutoReconnectIntroductionAck) {
version: V1
v1: <
type: AUTO_RECONNECT
auto_reconnect: <
event_type: CLIENT_INTRODUCTION_ACK
>
auto_reconnect: < event_type: CLIENT_INTRODUCTION_ACK >
>)pb";
ByteArray bytes = ForAutoReconnectIntroductionAck();
auto response = FromBytes(bytes);
@@ -652,6 +656,34 @@ TEST(OfflineFramesTest, CanGenerateAutoReconnectIntroductionAck) {
EXPECT_THAT(message, EqualsProto(kExpected));
}
TEST(OfflineFramesTest, CanGenerateBwuPathRequest) {
constexpr absl::string_view kExpected =
R"pb(
version: V1
v1: <
type: BANDWIDTH_UPGRADE_NEGOTIATION
bandwidth_upgrade_negotiation: <
event_type: UPGRADE_PATH_REQUEST
upgrade_path_info: <
upgrade_path_request: <
mediums: WIFI_HOTSPOT
medium_meta_data: <
medium_role: < support_wifi_hotspot_client: true >
>
>
>
>
>)pb";
std::vector<Medium> mediums;
mediums.push_back(Medium::WIFI_HOTSPOT);
MediumRole medium_role;
medium_role.set_support_wifi_hotspot_client(true);
ByteArray bytes = ForBwuPathRequest(mediums, medium_role);
auto response = FromBytes(bytes);
ASSERT_TRUE(response.ok());
OfflineFrame message = response.result();
EXPECT_THAT(message, EqualsProto(kExpected));
}
} // namespace
} // namespace parser