Files
hai007 7472c810d3 Correct AWDL related error codes
PiperOrigin-RevId: 791303156
2025-08-05 11:38:20 -07:00

304 lines
12 KiB
C++

// 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/strings/string_view.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/implementation/psk_info.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;
constexpr int kPasswordLength = 16;
constexpr absl::string_view kPskIdentity = "AwdlUpgradeMedium";
constexpr absl::string_view kAwdlServiceIdSuffixForServiceType = "_AWDL";
} // 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_AWDL_INVALID_CREDENTIAL)};
}
const UpgradePathInfo::AwdlCredentials& awdl_credentials =
upgrade_path_info.awdl_credentials();
if (!awdl_credentials.has_service_name() ||
!awdl_credentials.has_service_type() ||
!awdl_credentials.has_password()) {
LOG(ERROR) << "Failed to upgrade AWDL due to invalid credentials.";
return {
Error(OperationResultCode::CONNECTIVITY_AWDL_INVALID_CREDENTIAL)};
}
std::string service_name = awdl_credentials.service_name();
std::string service_type = awdl_credentials.service_type();
std::string password = awdl_credentials.password();
LOG(INFO) << "Attempting to connect to "
<< "AWDL (service_name:" << service_name
<< ", service_type:" << service_type
<< ", has password:" << (password.empty() ? "false" : "true")
<< ") for endpoint " << endpoint_id;
NsdServiceInfo nsd_service_info{};
nsd_service_info.SetServiceName(service_name);
nsd_service_info.SetServiceType(service_type);
api::PskInfo psk_info = {
.identity = std::string(kPskIdentity),
.password = password,
};
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_AWDL_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_AWDL_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, psk_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_AWDL_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)) {
api::PskInfo psk_info = {
.identity = std::string(kPskIdentity),
.password = GeneratePassword(),
};
if (!awdl_medium_.StartAcceptingConnections(
upgrade_service_id, psk_info,
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(
absl::StrCat(upgrade_service_id, kAwdlServiceIdSuffixForServiceType)));
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;
std::string password = credential.password;
LOG(INFO) << "Retrieved AWDL credentials. service_name: " << service_name
<< " and service_type: " << service_type
<< " and has password: " << (password.empty() ? "false" : "true");
// The AWDL upgraded medium is running under TLS, so we don't need to
// encryption for it again.
return parser::ForBwuAwdlPathAvailable(
service_name, service_type, password,
/*supports_disabling_encryption=*/true);
}
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;
}
std::string AwdlBwuHandler::GeneratePassword() {
std::string password_string;
ByteArray ramdon_bytes = Utils::GenerateRandomBytes(kPasswordLength);
for (auto byte : std::string(ramdon_bytes)) {
absl::StrAppend(&password_string, absl::StrFormat("%02X", byte));
}
return password_string;
}
} // namespace connections
} // namespace nearby