Add AWDL upgrade logic

PiperOrigin-RevId: 756335927
This commit is contained in:
Guogang Li
2025-05-08 09:08:52 -07:00
committed by Copybara-Service
parent 236e17988c
commit 7ea0e00333
11 changed files with 441 additions and 16 deletions
+11
View File
@@ -227,6 +227,12 @@ NC_INSTANCE NcCreateService() {
::nearby::connections::config_package_nearby::nearby_connections_feature::
kEnableAwdl,
true);
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
::nearby::connections::config_package_nearby::nearby_connections_feature::
kEnableStopBLEScanningOnWifiUpgrade,
true);
#endif
nc_context.router = new ::nearby::connections::ServiceControllerRouter();
@@ -484,6 +490,8 @@ void NcRequestConnection(
GetCppConnectionRequestInfo(instance, *connection_request_info, context);
::nearby::connections::ConnectionOptions cpp_connection_options;
cpp_connection_options.allowed.awdl =
connection_options->common_options.allowed_mediums[NC_MEDIUM_AWDL];
cpp_connection_options.allowed.ble =
connection_options->common_options.allowed_mediums[NC_MEDIUM_BLE];
cpp_connection_options.allowed.bluetooth =
@@ -492,6 +500,9 @@ void NcRequestConnection(
connection_options->common_options.allowed_mediums[NC_MEDIUM_WEB_RTC];
cpp_connection_options.allowed.wifi_lan =
connection_options->common_options.allowed_mediums[NC_MEDIUM_WIFI_LAN];
cpp_connection_options.allowed.wifi_hotspot =
connection_options->common_options
.allowed_mediums[NC_MEDIUM_WIFI_HOTSPOT];
cpp_connection_options.auto_upgrade_bandwidth =
connection_options->auto_upgrade_bandwidth;
cpp_connection_options.enforce_topology_constraints =
+2
View File
@@ -52,6 +52,7 @@ cc_library(
cc_library(
name = "internal",
srcs = [
"awdl_bwu_handler.cc",
"awdl_endpoint_channel.cc",
"base_bwu_handler.cc",
"base_endpoint_channel.cc",
@@ -93,6 +94,7 @@ cc_library(
"wifi_lan_service_info.cc",
],
hdrs = [
"awdl_bwu_handler.h",
"awdl_endpoint_channel.h",
"base_bwu_handler.h",
"base_endpoint_channel.h",
@@ -0,0 +1,270 @@
// Copyright 2025 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 "connections/implementation/awdl_bwu_handler.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/bind_front.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/time/time.h"
#include "connections/implementation/awdl_endpoint_channel.h"
#include "connections/implementation/base_bwu_handler.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/mediums/awdl.h"
#include "connections/implementation/mediums/mediums.h"
#include "connections/implementation/mediums/utils.h"
#include "connections/implementation/offline_frames.h"
#include "connections/implementation/service_id_constants.h"
#include "internal/platform/awdl.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/expected.h"
#include "internal/platform/logging.h"
#include "internal/platform/nsd_service_info.h"
namespace nearby {
namespace connections {
namespace {
using ::location::nearby::proto::connections::OperationResultCode;
constexpr absl::Duration kAwdlDiscoveryTimeout = absl::Seconds(5);
constexpr int kServiceNameLength = 8;
} // namespace
AwdlBwuHandler::AwdlBwuHandler(
Mediums& mediums, IncomingConnectionCallback incoming_connection_callback)
: BaseBwuHandler(std::move(incoming_connection_callback)),
mediums_(mediums) {}
// Called by BWU target. Retrieves a new medium info from incoming message,
// and establishes connection over AWDL using this info.
ErrorOr<std::unique_ptr<EndpointChannel>>
AwdlBwuHandler::CreateUpgradedEndpointChannel(
ClientProxy* client, const std::string& service_id,
const std::string& endpoint_id, const UpgradePathInfo& upgrade_path_info) {
std::string upgrade_service_id = WrapInitiatorUpgradeServiceId(service_id);
if (!upgrade_path_info.has_awdl_credentials()) {
return {
Error(OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL)};
}
const UpgradePathInfo::AwdlCredentials& awdl_credentials =
upgrade_path_info.awdl_credentials();
if (!awdl_credentials.has_service_name() ||
!awdl_credentials.has_service_type()) {
LOG(ERROR) << "Failed to upgrade AWDL due to invalid credentials.";
return {
Error(OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL)};
}
std::string service_name = awdl_credentials.service_name();
std::string service_type = awdl_credentials.service_type();
LOG(INFO) << "Attempting to connect to "
<< "AWDL (service_name:" << service_name
<< ", service_type:" << service_type << ") for endpoint "
<< endpoint_id;
NsdServiceInfo nsd_service_info{};
nsd_service_info.SetServiceName(service_name);
nsd_service_info.SetServiceType(service_type);
LOG(INFO) << "Start to discover the AWDL service "
"(service_name:"
<< service_name << ", service_type:" << service_type
<< ") for endpoint " << endpoint_id;
CountDownLatch latch(1);
// Needs to discover the upgrade service before connecting to it.
if (!awdl_medium_.StartDiscovery(
upgrade_service_id,
{
.service_discovered_cb =
[service_name, &latch](const NsdServiceInfo& service_info,
const std::string& service_id) {
if (service_info.GetServiceName() == service_name) {
LOG(INFO)
<< "Discovered the "
<< "AWDL service (service_name:" << service_name
<< ", service_type:" << service_info.GetServiceType()
<< ") successfully";
latch.CountDown();
}
},
.service_lost_cb = [](NsdServiceInfo service_info,
const std::string& service_id) {},
})) {
LOG(ERROR) << "Failed to discover the AWDL service (service_name:"
<< service_name << ", service_type:" << service_type
<< ") for endpoint " << endpoint_id;
return {Error(
OperationResultCode::NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE)};
}
if (!latch.Await(kAwdlDiscoveryTimeout)) {
LOG(ERROR) << "Failed to discover the AWDL service (service_name:"
<< service_name << ", service_type:" << service_type
<< ") due to timeout.";
awdl_medium_.StopDiscovery(upgrade_service_id);
return {Error(
OperationResultCode::NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE)};
}
LOG(INFO) << "Discovered the AWDL service "
"(service_name:"
<< service_name << ", service_type:" << service_type
<< ") for endpoint " << endpoint_id;
ErrorOr<AwdlSocket> socket_result =
awdl_medium_.Connect(upgrade_service_id, nsd_service_info,
client->GetCancellationFlag(endpoint_id));
if (socket_result.has_error()) {
LOG(ERROR) << "Failed to connect to the AWDL service (service_name:"
<< service_name << ", service_type:" << service_type
<< ") for endpoint " << endpoint_id;
awdl_medium_.StopDiscovery(upgrade_service_id);
return {Error(socket_result.error().operation_result_code().value())};
}
LOG(INFO) << "Connected to AWDL service (service_name:" << service_name
<< ", service_type:" << service_type
<< ") successfully while upgrading endpoint " << endpoint_id;
// Create a new AwdlEndpointChannel.
auto channel = std::make_unique<AwdlEndpointChannel>(
upgrade_service_id,
/*channel_name=*/upgrade_service_id, socket_result.value(), &awdl_medium_,
/*is_outgoing=*/true);
if (channel == nullptr) {
LOG(ERROR) << "Failed to create AWDL endpoint "
<< "channel to the AWDL service (service_name:" << service_name
<< ", service_type:" << service_type << ") for endpoint "
<< endpoint_id;
awdl_medium_.StopDiscovery(upgrade_service_id);
socket_result.value().Close();
return {Error(
OperationResultCode::NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE)};
}
return {std::move(channel)};
}
// Called by BWU initiator. Set up AWDL upgraded medium for this endpoint,
// and returns a upgrade path info (service_name, port) for remote party to
// perform discovery.
ByteArray AwdlBwuHandler::HandleInitializeUpgradedMediumForEndpoint(
ClientProxy* client, const std::string& upgrade_service_id,
const std::string& endpoint_id) {
if (!awdl_medium_.IsAcceptingConnections(upgrade_service_id)) {
if (!awdl_medium_.StartAcceptingConnections(
upgrade_service_id,
absl::bind_front(&AwdlBwuHandler::OnIncomingAwdlConnection, this,
client))) {
LOG(ERROR) << "Failed to initiate the AWDL upgrade for "
<< "service " << upgrade_service_id << " and endpoint "
<< endpoint_id
<< " because it failed to start listening for incoming AWDL "
"connections.";
return {};
}
// Need to advertise the service.
nsd_service_info_ = NsdServiceInfo();
nsd_service_info_.SetServiceName(GenerateServiceName());
nsd_service_info_.SetServiceType(GenerateServiceType(upgrade_service_id));
if (!awdl_medium_.StartAdvertising(upgrade_service_id, nsd_service_info_)) {
LOG(ERROR) << "Failed to initiate the AWDL upgrade for "
<< "service " << upgrade_service_id << " and endpoint "
<< endpoint_id << " because it failed to start advertising.";
awdl_medium_.StopAcceptingConnections(upgrade_service_id);
return {};
}
LOG(INFO) << "Started listening for incoming AWDL connections while "
"upgrading endpoint "
<< endpoint_id;
}
// Note: Credentials are not populated until StartAcceptingConnections() is
// called and the server socket is created. Be careful moving this codeblock
// around.
Awdl::AwdlCredential credential =
awdl_medium_.GetCredentials(upgrade_service_id);
std::string service_name = credential.service_name;
std::string service_type = credential.service_type;
LOG(INFO) << "Retrieved AWDL credentials. service_name: " << service_name
<< " and service_type: " << service_type;
return parser::ForBwuAwdlPathAvailable(service_name, service_type);
}
void AwdlBwuHandler::HandleRevertInitiatorStateForService(
const std::string& upgrade_service_id) {
awdl_medium_.StopAdvertising(upgrade_service_id);
awdl_medium_.StopAcceptingConnections(upgrade_service_id);
LOG(INFO) << "Reverted all states for "
<< "upgrade service ID " << upgrade_service_id << " successfully.";
}
// Accept Connection Callback.
void AwdlBwuHandler::OnIncomingAwdlConnection(
ClientProxy* client, const std::string& upgrade_service_id,
AwdlSocket socket) {
LOG(INFO) << "Accepted connection for upgrade service ID "
<< upgrade_service_id;
auto channel = std::make_unique<AwdlEndpointChannel>(
upgrade_service_id, /*channel_name=*/upgrade_service_id, socket,
&awdl_medium_, /*is_outgoing=*/false);
std::unique_ptr<IncomingSocketConnection> connection(
new IncomingSocketConnection{
.socket =
std::make_unique<AwdlIncomingSocket>(upgrade_service_id, socket),
.channel = std::move(channel),
});
NotifyOnIncomingConnection(client, std::move(connection));
}
std::string AwdlBwuHandler::GenerateServiceType(const std::string& service_id) {
std::string service_id_hash_string;
const ByteArray service_id_hash = Utils::Sha256Hash(
service_id, NsdServiceInfo::kTypeFromServiceIdHashLength);
for (auto byte : std::string(service_id_hash)) {
absl::StrAppend(&service_id_hash_string, absl::StrFormat("%02X", byte));
}
return absl::StrFormat(NsdServiceInfo::kNsdTypeFormat,
service_id_hash_string);
}
std::string AwdlBwuHandler::GenerateServiceName() {
std::string service_name_string;
ByteArray ramdon_bytes = Utils::GenerateRandomBytes(kServiceNameLength);
for (auto byte : std::string(ramdon_bytes)) {
absl::StrAppend(&service_name_string, absl::StrFormat("%02X", byte));
}
return service_name_string;
}
} // namespace connections
} // namespace nearby
@@ -0,0 +1,91 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_AWDL_BWU_HANDLER_H_
#define CORE_INTERNAL_AWDL_BWU_HANDLER_H_
#include <memory>
#include <string>
#include <utility>
#include "connections/implementation/base_bwu_handler.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/mediums/awdl.h"
#include "connections/implementation/mediums/mediums.h"
#include "internal/platform/awdl.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/expected.h"
#include "internal/platform/nsd_service_info.h"
namespace nearby {
namespace connections {
// Defines the set of methods that need to be implemented to handle the
// per-Medium-specific operations needed to upgrade an EndpointChannel.
class AwdlBwuHandler : public BaseBwuHandler {
public:
explicit AwdlBwuHandler(
Mediums& mediums,
IncomingConnectionCallback incoming_connection_callback);
private:
class AwdlIncomingSocket : public BwuHandler::IncomingSocket {
public:
explicit AwdlIncomingSocket(const std::string& name, AwdlSocket socket)
: name_(name), socket_(socket) {}
std::string ToString() override { return name_; }
void Close() override { socket_.Close(); }
private:
std::string name_;
AwdlSocket socket_;
};
// BwuHandler implementation:
ErrorOr<std::unique_ptr<EndpointChannel>> CreateUpgradedEndpointChannel(
ClientProxy* client, const std::string& service_id,
const std::string& endpoint_id,
const UpgradePathInfo& upgrade_path_info) override;
location::nearby::proto::connections::Medium GetUpgradeMedium() const final {
return location::nearby::proto::connections::Medium::AWDL;
}
void OnEndpointDisconnect(ClientProxy* client,
const std::string& endpoint_id) final {}
// BaseBwuHandler implementation:
ByteArray HandleInitializeUpgradedMediumForEndpoint(
ClientProxy* client, const std::string& upgrade_service_id,
const std::string& endpoint_id) final;
void HandleRevertInitiatorStateForService(
const std::string& upgrade_service_id) final;
void OnIncomingAwdlConnection(ClientProxy* client,
const std::string& upgrade_service_id,
AwdlSocket socket);
std::string GenerateServiceType(const std::string& service_id);
std::string GenerateServiceName();
Mediums& mediums_;
Awdl& awdl_medium_{mediums_.GetAwdl()};
NsdServiceInfo nsd_service_info_;
};
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_AWDL_BWU_HANDLER_H_
@@ -15,20 +15,32 @@
#include "connections/implementation/awdl_endpoint_channel.h"
#include <string>
#include <utility>
#include "connections/implementation/base_endpoint_channel.h"
#include "internal/platform/logging.h"
#include "connections/implementation/mediums/awdl.h"
#include "internal/platform/awdl.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace connections {
AwdlEndpointChannel::AwdlEndpointChannel(const std::string& service_id,
const std::string& channel_name,
AwdlSocket socket)
const std::string& channel_name,
AwdlSocket socket)
: BaseEndpointChannel(service_id, channel_name, &socket.GetInputStream(),
&socket.GetOutputStream()),
socket_(std::move(socket)) {}
AwdlEndpointChannel::AwdlEndpointChannel(const std::string& service_id,
const std::string& channel_name,
AwdlSocket socket, Awdl* awdl,
bool is_outgoing)
: BaseEndpointChannel(service_id, channel_name, &socket.GetInputStream(),
&socket.GetOutputStream()),
socket_(std::move(socket)),
awdl_(awdl),
is_outgoing_(is_outgoing) {}
location::nearby::proto::connections::Medium AwdlEndpointChannel::GetMedium()
const {
return location::nearby::proto::connections::Medium::AWDL;
@@ -37,15 +49,23 @@ location::nearby::proto::connections::Medium AwdlEndpointChannel::GetMedium()
void AwdlEndpointChannel::CloseImpl() {
auto status = socket_.Close();
if (!status.Ok()) {
NEARBY_LOGS(INFO)
<< "Failed to close underlying socket for AwdlEndpointChannel "
<< GetName() << " : exception = " << status.value;
LOG(INFO) << "Failed to close underlying socket for AwdlEndpointChannel "
<< GetName() << " : exception = " << status.value;
}
if (is_outgoing_ && awdl_ != nullptr) {
LOG(INFO) << "Stop AWDL discovery for outgoing channel.";
// Stops discovery on AWDL medium for the service id.
if (!awdl_->StopDiscovery(GetServiceId())) {
LOG(INFO) << "Failed to stop discovery for AwdlEndpointChannel "
<< GetName();
};
}
}
bool AwdlEndpointChannel::EnableMultiplexSocket() {
NEARBY_LOGS(INFO) << "AwdlEndpointChannel MultiplexSocket will be "
"enabled if the Awdl MultiplexSocket is valid";
LOG(INFO) << "AwdlEndpointChannel MultiplexSocket will be "
"enabled if the Awdl MultiplexSocket is valid";
socket_.EnableMultiplexSocket();
return true;
}
@@ -18,7 +18,7 @@
#include <string>
#include "connections/implementation/base_endpoint_channel.h"
#include "internal/platform/awdl.h"
#include "connections/implementation/mediums/awdl.h"
namespace nearby {
namespace connections {
@@ -27,7 +27,12 @@ class AwdlEndpointChannel final : public BaseEndpointChannel {
public:
// Creates both outgoing and incoming AWDL channels.
AwdlEndpointChannel(const std::string& service_id,
const std::string& channel_name, AwdlSocket socket);
const std::string& channel_name, AwdlSocket socket);
// Creates AWDL endpoint channel for bandwidth upgrade only.
AwdlEndpointChannel(const std::string& service_id,
const std::string& channel_name, AwdlSocket socket,
Awdl* awdl, bool is_outgoing);
location::nearby::proto::connections::Medium GetMedium() const override;
bool EnableMultiplexSocket() override;
@@ -36,6 +41,8 @@ class AwdlEndpointChannel final : public BaseEndpointChannel {
void CloseImpl() override;
AwdlSocket socket_;
Awdl* awdl_ = nullptr;
bool is_outgoing_ = false;
};
} // namespace connections
@@ -438,6 +438,7 @@ BooleanMediumSelector BasePcpHandler::ComputeIntersectionOfSupportedMediums(
mediumSelector.wifi_lan = intersection.contains(Medium::WIFI_LAN);
mediumSelector.wifi_hotspot = intersection.contains(Medium::WIFI_HOTSPOT);
mediumSelector.wifi_direct = intersection.contains(Medium::WIFI_DIRECT);
mediumSelector.awdl = intersection.contains(Medium::AWDL);
return mediumSelector;
}
+14
View File
@@ -26,6 +26,7 @@
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/connection_attempt_metadata_params.h"
#include "connections/implementation/awdl_bwu_handler.h"
#include "connections/implementation/bluetooth_bwu_handler.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/client_proxy.h"
@@ -107,6 +108,9 @@ BwuManager::BwuManager(
config_.allow_upgrade_to.wifi_direct = true;
config_.allow_upgrade_to.wifi_lan = true;
config_.allow_upgrade_to.wifi_hotspot = true;
#if defined(NC_IOS_SDK)
config_.allow_upgrade_to.awdl = true;
#endif // defined(NC_IOS_SDK)
}
if (!handlers.empty()) {
handlers_ = std::move(handlers);
@@ -126,6 +130,13 @@ BwuManager::~BwuManager() {
void BwuManager::InitBwuHandlers() {
// Register the supported concrete BwuMedium implementations.
if (config_.allow_upgrade_to.awdl) {
handlers_.emplace(
Medium::AWDL,
std::make_unique<AwdlBwuHandler>(
*mediums_,
absl::bind_front(&BwuManager::OnIncomingConnection, this)));
}
if (config_.allow_upgrade_to.wifi_hotspot) {
handlers_.emplace(
Medium::WIFI_HOTSPOT,
@@ -1474,6 +1485,9 @@ std::vector<Medium> BwuManager::StripOutUnavailableMediums(
bool available = false;
if (GetHandlerForMedium(m)) {
switch (m) {
case Medium::AWDL:
available = mediums_->GetAwdl().IsAvailable();
break;
case Medium::WIFI_LAN:
available = mediums_->GetWifiLan().IsAvailable();
break;
@@ -1072,12 +1072,13 @@ void P2pClusterPcpHandler::AwdlServiceDiscoveredHandler(
}
// Report the discovered endpoint to the client.
LOG(INFO) << "Found NsdServiceInfo " << service_info.GetServiceName()
<< " (with endpoint_id="
<< wifi_lan_service_info.GetEndpointId()
<< "and endpoint_info="
LOG(INFO) << "Found NsdServiceInfo "
<< "with (service_name:" << service_info.GetServiceName()
<< ", service_type:" << service_info.GetServiceType()
<< ", endpoint_id:" << wifi_lan_service_info.GetEndpointId()
<< ", endpoint_info:"
<< absl::BytesToHexString(
wifi_lan_service_info.GetEndpointInfo().data())
wifi_lan_service_info.GetEndpointInfo().AsStringView())
<< ").";
StopEndpointLostByMediumAlarm(wifi_lan_service_info.GetEndpointId(),
AWDL);
@@ -32,6 +32,11 @@ P2pPointToPointPcpHandler::P2pPointToPointPcpHandler(
std::vector<location::nearby::proto::connections::Medium>
P2pPointToPointPcpHandler::GetConnectionMediumsByPriority() {
std::vector<location::nearby::proto::connections::Medium> mediums;
#if defined(NC_IOS_SDK)
if (mediums_->GetAwdl().IsAvailable()) {
mediums.push_back(location::nearby::proto::connections::AWDL);
}
#endif // defined(NC_IOS_SDK)
if (mediums_->GetWifiLan().IsAvailable()) {
mediums.push_back(location::nearby::proto::connections::WIFI_LAN);
}
+4 -1
View File
@@ -72,6 +72,10 @@ struct BooleanMediumSelector {
std::vector<Medium> GetMediums(bool value) const {
std::vector<Medium> mediums;
// Mediums are sorted in order of decreasing preference.
// AWDL works on Apple devices only for now. Put it at the top of WIFI_LAN
// because it is the most preferred medium for Apple devices and no impact
// to other medium connections.
if (awdl == value) mediums.push_back(Medium::AWDL);
if (wifi_lan == value) mediums.push_back(Medium::WIFI_LAN);
if (wifi_direct == value) mediums.push_back(Medium::WIFI_DIRECT);
if (wifi_hotspot == value) mediums.push_back(Medium::WIFI_HOTSPOT);
@@ -86,7 +90,6 @@ struct BooleanMediumSelector {
}
if (bluetooth == value) mediums.push_back(Medium::BLUETOOTH);
if (ble == value) mediums.push_back(Medium::BLE);
if (awdl == value) mediums.push_back(Medium::AWDL);
return mediums;
}
};