diff --git a/cpp/core/internal/BUILD b/cpp/core/internal/BUILD index 40e734a8..17a10f2c 100644 --- a/cpp/core/internal/BUILD +++ b/cpp/core/internal/BUILD @@ -43,6 +43,7 @@ cc_library( "webrtc_bwu_handler.cc", "webrtc_endpoint_channel.cc", "wifi_lan_bwu_handler.cc", + "wifi_lan_bwu_handler_v2.cc", "wifi_lan_endpoint_channel.cc", "wifi_lan_endpoint_channel_v2.cc", "wifi_lan_service_info.cc", @@ -81,6 +82,7 @@ cc_library( "webrtc_bwu_handler.h", "webrtc_endpoint_channel.h", "wifi_lan_bwu_handler.h", + "wifi_lan_bwu_handler_v2.h", "wifi_lan_endpoint_channel.h", "wifi_lan_endpoint_channel_v2.h", "wifi_lan_service_info.h", diff --git a/cpp/core/internal/mediums/wifi_lan_v2.cc b/cpp/core/internal/mediums/wifi_lan_v2.cc index abe3c37b..8358cb16 100644 --- a/cpp/core/internal/mediums/wifi_lan_v2.cc +++ b/cpp/core/internal/mediums/wifi_lan_v2.cc @@ -351,8 +351,33 @@ WifiLanSocketV2 WifiLanV2::Connect(const std::string& service_id, const std::string& ip_address, int port, CancellationFlag* cancellation_flag) { MutexLock lock(&mutex_); + // Socket to return. To allow for NRVO to work, it has to be a single object. + WifiLanSocketV2 socket; - return {}; + if (service_id.empty()) { + NEARBY_LOGS(INFO) << "Refusing to create client WifiLan socket because " + "service_id is empty."; + return socket; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) << "Can't create client WifiLan socket [service_id=" + << service_id << "]; WifiLan isn't available."; + return socket; + } + + if (cancellation_flag->Cancelled()) { + NEARBY_LOGS(INFO) << "Can't create client WifiLan socket due to cancel."; + return socket; + } + + socket = medium_.ConnectToService(ip_address, port, cancellation_flag); + if (!socket.IsValid()) { + NEARBY_LOGS(INFO) << "Failed to Connect via WifiLan [service_id=" + << service_id << "]"; + } + + return socket; } std::pair WifiLanV2::GetCredentials( diff --git a/cpp/core/internal/wifi_lan_bwu_handler_v2.cc b/cpp/core/internal/wifi_lan_bwu_handler_v2.cc new file mode 100644 index 00000000..2ed36bc5 --- /dev/null +++ b/cpp/core/internal/wifi_lan_bwu_handler_v2.cc @@ -0,0 +1,169 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "core/internal/wifi_lan_bwu_handler_v2.h" + +#include +#include + +#include "absl/functional/bind_front.h" +#include "core/internal/client_proxy.h" +#include "core/internal/mediums/utils.h" +#include "core/internal/offline_frames.h" +#include "core/internal/wifi_lan_endpoint_channel_v2.h" +#include "platform/public/wifi_lan_v2.h" + +namespace location { +namespace nearby { +namespace connections { + +WifiLanV2BwuHandler::WifiLanV2BwuHandler( + Mediums& mediums, EndpointChannelManager& channel_manager, + BwuNotifications notifications) + : BaseBwuHandler(channel_manager, std::move(notifications)), + mediums_(mediums) {} + +// Called by BWU initiator. Set up WifiLan upgraded medium for this endpoint, +// and returns a upgrade path info (ip address, port) for remote party to +// perform discovery. +ByteArray WifiLanV2BwuHandler::InitializeUpgradedMediumForEndpoint( + ClientProxy* client, const std::string& service_id, + const std::string& endpoint_id) { + // Use wrapped service ID to avoid have the same ID with the one for + // startAdvertising. Otherwise, the listening request would be ignored because + // the medium already start accepting the connection because the client not + // stop the advertising yet. + std::string upgrade_service_id = Utils::WrapUpgradeServiceId(service_id); + + auto credential = wifi_lan_medium_.GetCredentials(upgrade_service_id); + auto ip_address = credential.first; + auto port = credential.second; + if (ip_address.empty()) { + NEARBY_LOGS(INFO) + << "WifiLanBwuHandler couldn't initiate the wifi_lan upgrade for " + "endpoint " + << endpoint_id + << " because the wifi_lan ip address were unable to be obtained."; + return {}; + } + + if (!wifi_lan_medium_.IsAcceptingConnections(upgrade_service_id)) { + if (!wifi_lan_medium_.StartAcceptingConnections( + upgrade_service_id, + { + .accepted_cb = absl::bind_front( + &WifiLanV2BwuHandler::OnIncomingWifiLanConnection, this, + client, service_id), + })) { + NEARBY_LOGS(ERROR) + << "WifiLanBwuHandler couldn't initiate the WifiLan upgrade for " + "endpoint " + << endpoint_id + << " because it failed to start listening for " + "incoming WifiLan connections."; + return {}; + } + NEARBY_LOGS(INFO) + << "WifiLanBwuHandler successfully started listening for incoming " + "WifiLan connections while upgrading endpoint " + << endpoint_id; + } + + // cache service ID to revert + active_service_ids_.insert(upgrade_service_id); + + return parser::ForBwuWifiLanPathAvailable(ip_address, port); +} + +void WifiLanV2BwuHandler::Revert() { + for (const std::string& service_id : active_service_ids_) { + wifi_lan_medium_.StopAcceptingConnections(service_id); + } + active_service_ids_.clear(); + + NEARBY_LOG(INFO, "WifiLanBwuHandler successfully reverted all states."); +} + +// Called by BWU target. Retrieves a new medium info from incoming message, +// and establishes connection over WifiLan using this info. +std::unique_ptr +WifiLanV2BwuHandler::CreateUpgradedEndpointChannel( + ClientProxy* client, const std::string& service_id, + const std::string& endpoint_id, const UpgradePathInfo& upgrade_path_info) { + if (!upgrade_path_info.has_wifi_lan_socket()) { + return nullptr; + } + const UpgradePathInfo::WifiLanSocket& upgrade_path_info_socket = + upgrade_path_info.wifi_lan_socket(); + if (!upgrade_path_info_socket.has_ip_address() || + !upgrade_path_info_socket.has_wifi_port()) { + NEARBY_LOG(ERROR, "WifiLanBwuHandler failed to parse UpgradePathInfo."); + return nullptr; + } + + const std::string& ip_address = upgrade_path_info_socket.ip_address(); + std::int32_t port = upgrade_path_info_socket.wifi_port(); + + NEARBY_LOGS(VERBOSE) << "WifiLanBwuHandler is attempting to connect to " + "available WifiLan service (" + << ip_address << ":" << port << ") for endpoint " + << endpoint_id; + + WifiLanSocketV2 socket = wifi_lan_medium_.Connect( + service_id, ip_address, port, client->GetCancellationFlag(endpoint_id)); + if (!socket.IsValid()) { + NEARBY_LOGS(ERROR) + << "WifiLanBwuHandler failed to connect to the WifiLan service (" + << ip_address << ":" << port << ") for endpoint " << endpoint_id; + return nullptr; + } + + NEARBY_LOGS(VERBOSE) + << "WifiLanBwuHandler successfully connected to WifiLan service (" + << ip_address << ":" << port << ") while upgrading endpoint " + << endpoint_id; + + // Create a new WifiLanEndpointChannel. + auto channel = + absl::make_unique(service_id, socket); + if (channel == nullptr) { + NEARBY_LOGS(ERROR) << "WifiLanBwuHandler failed to create WifiLan endpoint " + "channel to the WifiLan service (" + << ip_address << ":" << port << ") for endpoint " + << endpoint_id; + socket.Close(); + return nullptr; + } + + return channel; +} + +// Accept Connection Callback. +void WifiLanV2BwuHandler::OnIncomingWifiLanConnection( + ClientProxy* client, const std::string& service_id, + WifiLanSocketV2 socket) { + auto channel = + absl::make_unique(service_id, socket); + std::unique_ptr connection( + new IncomingSocketConnection{ + .socket = + absl::make_unique(service_id, socket), + .channel = std::move(channel), + }); + bwu_notifications_.incoming_connection_cb(client, std::move(connection)); +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core/internal/wifi_lan_bwu_handler_v2.h b/cpp/core/internal/wifi_lan_bwu_handler_v2.h new file mode 100644 index 00000000..5474eaac --- /dev/null +++ b/cpp/core/internal/wifi_lan_bwu_handler_v2.h @@ -0,0 +1,80 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ +#define CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ + +#include "core/internal/base_bwu_handler.h" +#include "core/internal/client_proxy.h" +#include "core/internal/endpoint_channel_manager.h" +#include "core/internal/mediums/mediums.h" + +namespace location { +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 WifiLanV2BwuHandler : public BaseBwuHandler { + public: + WifiLanV2BwuHandler(Mediums& mediums, EndpointChannelManager& channel_manager, + BwuNotifications notifications); + ~WifiLanV2BwuHandler() override = default; + + private: + ByteArray InitializeUpgradedMediumForEndpoint( + ClientProxy* client, const std::string& service_id, + const std::string& endpoint_id) override; + + void Revert() override; + + std::unique_ptr CreateUpgradedEndpointChannel( + ClientProxy* client, const std::string& service_id, + const std::string& endpoint_id, + const UpgradePathInfo& upgrade_path_info) override; + + Medium GetUpgradeMedium() const override { return Medium::MDNS; } + + void OnEndpointDisconnect(ClientProxy* client, + const std::string& endpoint_id) override {} + + void OnIncomingWifiLanConnection(ClientProxy* client, + const std::string& service_id, + WifiLanSocketV2 socket); + + class WifiLanV2IncomingSocket : public BwuHandler::IncomingSocket { + public: + explicit WifiLanV2IncomingSocket(const std::string& name, + WifiLanSocketV2 socket) + : name_(name), socket_(socket) {} + ~WifiLanV2IncomingSocket() override = default; + + std::string ToString() override { return name_; } + void Close() override { socket_.Close(); } + + private: + std::string name_; + WifiLanSocketV2 socket_; + }; + + Mediums& mediums_; + WifiLanV2& wifi_lan_medium_{mediums_.GetWifiLanV2()}; + absl::flat_hash_set active_service_ids_; +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ diff --git a/cpp/platform/base/medium_environment.h b/cpp/platform/base/medium_environment.h index cf198afe..8f25ec64 100644 --- a/cpp/platform/base/medium_environment.h +++ b/cpp/platform/base/medium_environment.h @@ -245,7 +245,7 @@ class MediumEnvironment { api::WifiLanSocket& socket, const std::string& service_id); - // Returns WiFi LAN service matching IP address and port, or nullptr. + // Returns WifiLan service matching IP address and port, or nullptr. api::WifiLanService* GetWifiLanService(const std::string& ip_address, int port); @@ -278,7 +278,8 @@ class MediumEnvironment { // Removes medium-related info. This should correspond to device power off. void UnregisterWifiLanMediumV2(api::WifiLanMediumV2& medium); - // Returns WiFi LAN service matching IP address and port, or nullptr. + // Returns WifiLan medium whose advertising service matching IP address and + // port, or nullptr. api::WifiLanMediumV2* GetWifiLanV2Medium(const std::string& ip_address, int port); diff --git a/cpp/platform/impl/g3/wifi_lan_v2.cc b/cpp/platform/impl/g3/wifi_lan_v2.cc index e94727af..00b4bac7 100644 --- a/cpp/platform/impl/g3/wifi_lan_v2.cc +++ b/cpp/platform/impl/g3/wifi_lan_v2.cc @@ -293,9 +293,9 @@ std::unique_ptr WifiLanMediumV2::ConnectToService( std::unique_ptr WifiLanMediumV2::ConnectToService( const std::string& ip_address, int port, CancellationFlag* cancellation_flag) { + std::string socket_name = WifiLanServerSocketV2::GetName(ip_address, port); NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService [self]: medium=" << this - << ", ip address + port=" - << WifiLanServerSocketV2::GetName(ip_address, port); + << ", ip address + port=" << socket_name; // First, find an instance of remote medium, that exposed this service. auto& env = MediumEnvironment::Instance(); auto* remote_medium = @@ -306,10 +306,9 @@ std::unique_ptr WifiLanMediumV2::ConnectToService( WifiLanServerSocketV2* server_socket = nullptr; NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService [peer]: medium=" - << remote_medium << ", remote ip address + port=" - << WifiLanServerSocketV2::GetName(ip_address, port); + << remote_medium + << ", remote ip address + port=" << socket_name; // Then, find our server socket context in this medium. - std::string socket_name = WifiLanServerSocketV2::GetName(ip_address, port); { absl::MutexLock medium_lock(&remote_medium->mutex_); auto item = remote_medium->server_sockets_.find(socket_name);