From 2c47104dc9e2e5b58d586406d47c33c1dad0c225 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 16 Feb 2023 13:26:13 -0800 Subject: [PATCH] Update the Apple Wi-Fi LAN medium to use WifiLanV2 PiperOrigin-RevId: 510228266 --- internal/platform/implementation/apple/BUILD | 6 +- .../platform/implementation/apple/wifi_lan.h | 176 ++---- .../platform/implementation/apple/wifi_lan.mm | 519 +++++------------- 3 files changed, 183 insertions(+), 518 deletions(-) diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 6627e5aa..2d0dbe3e 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -61,14 +61,10 @@ objc_library( "//internal/platform/implementation/shared:file", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", + "//third_party/apple_frameworks:Network", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/container:common", "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", ], diff --git a/internal/platform/implementation/apple/wifi_lan.h b/internal/platform/implementation/apple/wifi_lan.h index a7b20d0a..3d38a8f3 100644 --- a/internal/platform/implementation/apple/wifi_lan.h +++ b/internal/platform/implementation/apple/wifi_lan.h @@ -16,185 +16,119 @@ #define PLATFORM_IMPL_APPLE_WIFI_LAN_H_ #import +#import + #include +#include -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" // IWYU pragma: export #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/nsd_service_info.h" @class GNCMBonjourBrowser; @class GNCMBonjourService; +@class GNCWiFiLANMedium; +@class GNCWiFiLANServerSocket; +@class GNCWiFiLANSocket; namespace nearby { namespace apple { -/** InputStream that reads from GNCMConnection. */ +/** + * InputStream that reads from GNCMConnection. + */ class WifiLanInputStream : public InputStream { public: - WifiLanInputStream(); - ~WifiLanInputStream() override; + explicit WifiLanInputStream(GNCWiFiLANSocket* socket); + ~WifiLanInputStream() override = default; ExceptionOr Read(std::int64_t size) override; Exception Close() override; - GNCMConnectionHandlers* GetConnectionHandlers() { return connectionHandlers_; } - private: - GNCMConnectionHandlers* connectionHandlers_; - NSMutableArray* newDataPackets_; - NSMutableData* accumulatedData_; - NSCondition* condition_; + GNCWiFiLANSocket* socket_; }; -/** OutputStream that writes to GNCMConnection. */ +/** + * OutputStream that writes to GNCMConnection. + */ class WifiLanOutputStream : public OutputStream { public: - explicit WifiLanOutputStream(id connection) - : connection_(connection), condition_([[NSCondition alloc] init]) {} - ~WifiLanOutputStream() override; + explicit WifiLanOutputStream(GNCWiFiLANSocket* socket); + ~WifiLanOutputStream() override = default; Exception Write(const ByteArray& data) override; Exception Flush() override; Exception Close() override; private: - id connection_; - NSCondition* condition_; + GNCWiFiLANSocket* socket_; }; -/** Concrete WifiLanSocket implementation. */ +/** + * Concrete WifiLanSocket implementation. + */ class WifiLanSocket : public api::WifiLanSocket { public: - WifiLanSocket() = default; - explicit WifiLanSocket(id connection); - ~WifiLanSocket() override; + explicit WifiLanSocket(GNCWiFiLANSocket* socket); + ~WifiLanSocket() override = default; - // api::WifiLanSocket: - InputStream& GetInputStream() override { return *input_stream_; } - OutputStream& GetOutputStream() override { return *output_stream_; } - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override; + OutputStream& GetOutputStream() override; + Exception Close() override; private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; + GNCWiFiLANSocket* socket_; std::unique_ptr input_stream_; std::unique_ptr output_stream_; }; -/** Concrete WifiLanServerSocket implementation. */ +/** + * Concrete WifiLanServerSocket implementation. + */ class WifiLanServerSocket : public api::WifiLanServerSocket { public: - static std::string GetName(const std::string& ip_address, int port); + explicit WifiLanServerSocket(GNCWiFiLANServerSocket* server_socket); + ~WifiLanServerSocket() override = default; - ~WifiLanServerSocket() override; - - // api::WifiLanServerSocket: - std::string GetIPAddress() const override ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - return ip_address_; - } - void SetIPAddress(const std::string& ip_address) ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - ip_address_ = ip_address; - } - int GetPort() const override ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - return port_; - } - void SetPort(int port) ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - port_ = port; - } - std::unique_ptr Accept() override ABSL_LOCKS_EXCLUDED(mutex_); - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - bool Connect(std::unique_ptr socket) ABSL_LOCKS_EXCLUDED(mutex_); - void SetCloseNotifier(absl::AnyInvocable notifier) ABSL_LOCKS_EXCLUDED(mutex_); + std::string GetIPAddress() const override; + int GetPort() const override; + std::unique_ptr Accept() override; + Exception Close() override; private: - Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - std::string ip_address_ ABSL_GUARDED_BY(mutex_); - int port_ ABSL_GUARDED_BY(mutex_); - absl::CondVar cond_; - absl::flat_hash_set> pending_sockets_ ABSL_GUARDED_BY(mutex_); - absl::AnyInvocable close_notifier_ ABSL_GUARDED_BY(mutex_); - bool closed_ ABSL_GUARDED_BY(mutex_) = false; + GNCWiFiLANServerSocket* server_socket_; }; -/** Concrete WifiLanMedium implementation. */ +/** + * Concrete WifiLanMedium implementation. + */ class WifiLanMedium : public api::WifiLanMedium { public: - WifiLanMedium() = default; - ~WifiLanMedium() override; + WifiLanMedium(); + ~WifiLanMedium() override = default; WifiLanMedium(const WifiLanMedium&) = delete; WifiLanMedium& operator=(const WifiLanMedium&) = delete; - // Check if a network connection to a primary router exist. bool IsNetworkConnected() const override { return true; } - - // api::WifiLanMedium: - bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override - ABSL_LOCKS_EXCLUDED(mutex_); - bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override ABSL_LOCKS_EXCLUDED(mutex_); - - bool StartDiscovery(const std::string& service_type, DiscoveredServiceCallback callback) override - ABSL_LOCKS_EXCLUDED(mutex_); - bool StopDiscovery(const std::string& service_type) override ABSL_LOCKS_EXCLUDED(mutex_); - - std::unique_ptr ConnectToService( - const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override; - std::unique_ptr ConnectToService( - const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override; - std::unique_ptr ListenForService(int port) override - ABSL_LOCKS_EXCLUDED(mutex_); - absl::optional> GetDynamicPortRange() override { return absl::nullopt; } + bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override; + bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override; + bool StartDiscovery(const std::string& service_type, DiscoveredServiceCallback callback) override; + bool StopDiscovery(const std::string& service_type) override; + std::unique_ptr ConnectToService( + const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override; + std::unique_ptr ConnectToService( + const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override; + std::unique_ptr ListenForService(int port) override; + private: - struct AdvertisingInfo { - bool Empty() const { return services.empty(); } - void Clear() { services.clear(); } - void Add(const std::string& service_type, GNCMBonjourService* service) { - services.insert({service_type, service}); - } - void Remove(const std::string& service_type) { services.erase(service_type); } - bool Existed(const std::string& service_type) const { return services.contains(service_type); } - - absl::flat_hash_map services; - }; - struct DiscoveringInfo { - bool Empty() const { return services.empty(); } - void Clear() { services.clear(); } - void Add(const std::string& service_type, GNCMBonjourBrowser* browser) { - services.insert({service_type, browser}); - } - void Remove(const std::string& service_type) { services.erase(service_type); } - bool Existed(const std::string& service_type) const { return services.contains(service_type); } - - absl::flat_hash_map services; - }; - - std::string GetFakeIPAddress() const; - int GetFakePort() const; - - absl::Mutex mutex_; - AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); - int requesting_port_ = 0; - DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_); - absl::flat_hash_map server_sockets_ ABSL_GUARDED_BY(mutex_); - absl::flat_hash_map connection_requesters_ - ABSL_GUARDED_BY(mutex_); + GNCWiFiLANMedium* medium_; + absl::AnyInvocable service_discovered_cb_; + absl::AnyInvocable service_lost_cb_; }; } // namespace apple diff --git a/internal/platform/implementation/apple/wifi_lan.mm b/internal/platform/implementation/apple/wifi_lan.mm index 641e5b2b..c6d6b98b 100644 --- a/internal/platform/implementation/apple/wifi_lan.mm +++ b/internal/platform/implementation/apple/wifi_lan.mm @@ -18,478 +18,213 @@ #include #include -#include "absl/container/flat_hash_map.h" -#include "absl/container/internal/common.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/synchronization/mutex.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/exception.h" -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" -#import "internal/platform/implementation/apple/Mediums/WifiLan/GNCMBonjourBrowser.h" -#import "internal/platform/implementation/apple/Mediums/WifiLan/GNCMBonjourService.h" -#include "internal/platform/implementation/apple/utils.h" -#include "internal/platform/implementation/wifi_lan.h" -#include "internal/platform/nsd_service_info.h" -#include "internal/platform/prng.h" +#import "internal/platform/implementation/apple/Mediums/WifiLanV2/GNCWiFiLANMedium.h" +#import "internal/platform/implementation/apple/Mediums/WifiLanV2/GNCWiFiLANServerSocket.h" +#import "internal/platform/implementation/apple/Mediums/WifiLanV2/GNCWiFiLANSocket.h" #import "GoogleToolboxForMac/GTMLogger.h" namespace nearby { namespace apple { -/** WifiLanInputStream implementation. */ -WifiLanInputStream::WifiLanInputStream() - : newDataPackets_([NSMutableArray array]), - accumulatedData_([NSMutableData data]), - condition_([[NSCondition alloc] init]) { - // Create the handlers of incoming data from the remote endpoint. - connectionHandlers_ = [GNCMConnectionHandlers - payloadHandler:^(NSData* data) { - [condition_ lock]; - // Add the incoming data to the data packet array to be processed in Read() below. - [newDataPackets_ addObject:data]; - [condition_ signal]; - [condition_ unlock]; - } - disconnectedHandler:^{ - [condition_ lock]; - // Release the data packet array, meaning the stream has been closed or severed. - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; - }]; -} +#pragma mark - WifiLanInputStream -WifiLanInputStream::~WifiLanInputStream() { - NSCAssert(!newDataPackets_, @"WifiLanInputStream not closed before destruction"); -} +WifiLanInputStream::WifiLanInputStream(GNCWiFiLANSocket* socket) : socket_(socket) {} ExceptionOr WifiLanInputStream::Read(std::int64_t size) { - // Block until either (a) the connection has been closed, or (b) enough data to return. - NSData* dataToReturn; - [condition_ lock]; - while (true) { - // Check if the stream has been closed or severed. - if (!newDataPackets_) break; - - if (newDataPackets_.count > 0) { - // Add the packet data to the accumulated data. - for (NSData* data in newDataPackets_) { - if (data.length > 0) { - [accumulatedData_ appendData:data]; - } - } - [newDataPackets_ removeAllObjects]; - } - - if ((size == -1) && (accumulatedData_.length > 0)) { - // Return all of the data. - dataToReturn = accumulatedData_; - accumulatedData_ = [NSMutableData data]; - break; - } else if (accumulatedData_.length > 0) { - // Return up to |size| bytes of the data. - std::int64_t sizeToReturn = accumulatedData_.length < size ? accumulatedData_.length : size; - NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); - dataToReturn = [accumulatedData_ subdataWithRange:range]; - [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; - break; - } - - [condition_ wait]; - } - [condition_ unlock]; - - if (dataToReturn) { - GTMLoggerInfo(@"[NEARBY] Input stream: Received data of size: %lu", - (unsigned long)dataToReturn.length); - return ExceptionOr(ByteArrayFromNSData(dataToReturn)); - } else { - return ExceptionOr{Exception::kIo}; + NSError* error = nil; + NSData* data = [socket_ readMaxLength:size error:&error]; + if (data == nil) { + GTMLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; } + return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } Exception WifiLanInputStream::Close() { - // Unblock pending read operation. - [condition_ lock]; - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; + // The input stream reads directly from the connection. It can not be closed without closing the + // connection itself. A call to `WifiLanSocket::Close` will close the connection. return {Exception::kSuccess}; } -/** WifiLanOutputStream implementation. */ -WifiLanOutputStream::~WifiLanOutputStream() { - NSCAssert(!connection_, @"WifiLanOutputStream not closed before destruction"); -} +#pragma mark - WifiLanOutputStream + +WifiLanOutputStream::WifiLanOutputStream(GNCWiFiLANSocket* socket) : socket_(socket) {} Exception WifiLanOutputStream::Write(const ByteArray& data) { - [condition_ lock]; - GTMLoggerDebug(@"[NEARBY] Sending data of size: %lu", - (unsigned long)NSDataFromByteArray(data).length); - - NSMutableData* packet = [NSMutableData dataWithData:NSDataFromByteArray(data)]; - - // Send the data, blocking until the completion handler is called. - __block GNCMPayloadResult sendResult = GNCMPayloadResultFailure; - __block bool isComplete = NO; - NSCondition* condition = condition_; // don't capture |this| in completion - // Check if connection_ is nil, then just don't wait and return as failure. - if (connection_ != nil) { - [connection_ sendData:packet - progressHandler:^(size_t count) { - } - completion:^(GNCMPayloadResult result) { - // Make sure we haven't already reported completion before. This prevents a crash - // where we try leaving a dispatch group more times than we entered it. - // b/79095653. - if (isComplete) { - return; - } - isComplete = YES; - sendResult = result; - [condition lock]; - [condition signal]; - [condition unlock]; - }]; - [condition_ wait]; - [condition_ unlock]; - } else { - sendResult = GNCMPayloadResultFailure; - [condition_ unlock]; - } - - if (sendResult == GNCMPayloadResultSuccess) { - return {Exception::kSuccess}; - } else { + NSError* error = nil; + BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + if (!result) { + GTMLoggerError(@"Error writing socket: %@", error); return {Exception::kIo}; } + return {Exception::kSuccess}; } Exception WifiLanOutputStream::Flush() { - // The Write() function block until the data is received by the remote endpoint, so there's - // nothing to do here. + // Write blocks until the data has successfully been written/received, so no more work is needed + // to flush. return {Exception::kSuccess}; } Exception WifiLanOutputStream::Close() { - // Unblock pending write operation. - [condition_ lock]; - connection_ = nil; - [condition_ signal]; - [condition_ unlock]; + // The output stream writes directly to the connection. It can not be closed without closing the + // connection itself. A call to `WifiLanSocket::Close` will close the connection. return {Exception::kSuccess}; } -/** WifiLanSocket implementation. */ -WifiLanSocket::WifiLanSocket(id connection) - : input_stream_(new WifiLanInputStream()), - output_stream_(new WifiLanOutputStream(connection)) {} +#pragma mark - WifiLanSocket -WifiLanSocket::~WifiLanSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} +WifiLanSocket::WifiLanSocket(GNCWiFiLANSocket* socket) + : socket_(socket), + input_stream_(std::make_unique(socket)), + output_stream_(std::make_unique(socket)) {} -bool WifiLanSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} +InputStream& WifiLanSocket::GetInputStream() { return *input_stream_; } + +OutputStream& WifiLanSocket::GetOutputStream() { return *output_stream_; } Exception WifiLanSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); + [socket_ close]; return {Exception::kSuccess}; } -void WifiLanSocket::DoClose() { - if (!closed_) { - input_stream_->Close(); - output_stream_->Close(); - closed_ = true; - } +#pragma mark - WifiLanServerSocket + +WifiLanServerSocket::WifiLanServerSocket(GNCWiFiLANServerSocket* server_socket) + : server_socket_(server_socket) {} + +std::string WifiLanServerSocket::GetIPAddress() const { + return [server_socket_.ipAddress UTF8String]; } -/** WifiLanServerSocket implementation. */ -std::string WifiLanServerSocket::GetName(const std::string& ip_address, int port) { - std::string dot_delimited_string; - if (!ip_address.empty()) { - for (auto byte : ip_address) { - if (!dot_delimited_string.empty()) absl::StrAppend(&dot_delimited_string, "."); - absl::StrAppend(&dot_delimited_string, absl::StrFormat("%d", byte)); - } - } - std::string out = absl::StrCat(dot_delimited_string, ":", port); - return out; -} - -WifiLanServerSocket::~WifiLanServerSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} +int WifiLanServerSocket::GetPort() const { return server_socket_.port; } std::unique_ptr WifiLanServerSocket::Accept() { - absl::MutexLock lock(&mutex_); - while (!closed_ && pending_sockets_.empty()) { - cond_.Wait(&mutex_); + NSError* error = nil; + GNCWiFiLANSocket* socket = [server_socket_ acceptWithError:&error]; + if (socket != nil) { + return std::make_unique(socket); } - // Return early if closed. - if (closed_) return {}; - - auto remote_socket = std::move(pending_sockets_.extract(pending_sockets_.begin()).value()); - return std::move(remote_socket); -} - -bool WifiLanServerSocket::Connect(std::unique_ptr socket) { - absl::MutexLock lock(&mutex_); - if (closed_) { - return false; + if (error != nil) { + GTMLoggerError(@"Error accepting socket: %@", error); } - // add client socket to the pending list - pending_sockets_.insert(std::move(socket)); - cond_.SignalAll(); - if (closed_) { - return false; - } - return true; -} - -void WifiLanServerSocket::SetCloseNotifier(absl::AnyInvocable notifier) { - absl::MutexLock lock(&mutex_); - close_notifier_ = std::move(notifier); + return nil; } Exception WifiLanServerSocket::Close() { - absl::MutexLock lock(&mutex_); - return DoClose(); -} - -Exception WifiLanServerSocket::DoClose() { - bool should_notify = !closed_; - closed_ = true; - if (should_notify) { - cond_.SignalAll(); - if (close_notifier_) { - auto notifier = std::move(close_notifier_); - mutex_.Unlock(); - // Notifier may contain calls to public API, and may cause deadlock, if - // mutex_ is held during the call. - notifier(); - mutex_.Lock(); - } - } + [server_socket_ close]; return {Exception::kSuccess}; } -/** WifiLanMedium implementation. */ -WifiLanMedium::~WifiLanMedium() { - advertising_info_.Clear(); - discovering_info_.Clear(); -} +#pragma mark - WifiLanMedium + +WifiLanMedium::WifiLanMedium() : medium_([[GNCWiFiLANMedium alloc] init]) {} bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { - std::string service_type = nsd_service_info.GetServiceType(); - NSString* serviceType = ObjCStringFromCppString(service_type); - { - absl::MutexLock lock(&mutex_); - if (advertising_info_.Existed(service_type)) { - GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start advertising because " - @"service_type=%@, has started already", - serviceType); - return false; - } - } - - // Retrieve service name. - NSString* serviceName = ObjCStringFromCppString(nsd_service_info.GetServiceName()); - // Retrieve TXTRecord and convert it to NSDictionary type. - NSDictionary* TXTRecordData = - NSDictionaryFromCppTxtRecords(nsd_service_info.GetTxtRecords()); - // Get ip address and port to retrieve the server_socket, if not, then return nil. - std::string ip_address = nsd_service_info.GetIPAddress(); - int port = nsd_service_info.GetPort(); - __block std::string socket_name = WifiLanServerSocket::GetName(ip_address, port); - GNCMBonjourService* service = [[GNCMBonjourService alloc] - initWithServiceName:serviceName - serviceType:serviceType - port:requesting_port_ - TXTRecordData:TXTRecordData - endpointConnectedHandler:^GNCMConnectionHandlers*(id connection) { - auto item = server_sockets_.find(socket_name); - WifiLanServerSocket* server_socket = item != server_sockets_.end() ? item->second : nullptr; - if (!server_socket) { - return nil; - } - auto socket = absl::make_unique(connection); - GNCMConnectionHandlers* connectionHandlers = - static_cast(socket->GetInputStream()).GetConnectionHandlers(); - server_socket->Connect(std::move(socket)); - return connectionHandlers; - }]; - { - absl::MutexLock lock(&mutex_); - advertising_info_.Add(service_type, service); + NSInteger port = nsd_service_info.GetPort(); + NSString* serviceName = @(nsd_service_info.GetServiceName().c_str()); + NSString* serviceType = @(nsd_service_info.GetServiceType().c_str()); + NSMutableDictionary* txtRecords = [[NSMutableDictionary alloc] init]; + for (const auto& record : nsd_service_info.GetTxtRecords()) { + [txtRecords setObject:@(record.second.c_str()) forKey:@(record.first.c_str())]; } + [medium_ startAdvertisingPort:port + serviceName:serviceName + serviceType:serviceType + txtRecords:txtRecords]; return true; } bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) { - std::string service_type = nsd_service_info.GetServiceType(); - { - absl::MutexLock lock(&mutex_); - if (!advertising_info_.Existed(service_type)) { - GTMLoggerInfo(@"[NEARBY] WifiLan StopAdvertising: Can't stop advertising because we never " - @"started advertising for service_type=%@", - ObjCStringFromCppString(service_type)); - return false; - } - advertising_info_.Remove(service_type); - } + NSInteger port = nsd_service_info.GetPort(); + [medium_ stopAdvertisingPort:port]; return true; } bool WifiLanMedium::StartDiscovery(const std::string& service_type, DiscoveredServiceCallback callback) { - NSString* serviceType = ObjCStringFromCppString(service_type); - { - absl::MutexLock lock(&mutex_); - if (discovering_info_.Existed(service_type)) { - GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start discovery because " - @"service_type=%@, has started already", - serviceType); - return false; - } - } + __block NSString* serviceType = @(service_type.c_str()); __block DiscoveredServiceCallback client_callback = std::move(callback); - GNCMBonjourBrowser* browser = [[GNCMBonjourBrowser alloc] - initWithServiceType:serviceType - endpointFoundHandler:^GNCMEndpointLostHandler( - NSString* endpointId, NSString* serviceType, NSString* serviceName, - NSDictionary* _Nullable txtRecordData, - GNCMConnectionRequester requestConnection) { - __block NsdServiceInfo nsd_service_info = {}; - nsd_service_info.SetServiceName(CppStringFromObjCString(serviceName)); - nsd_service_info.SetServiceType(CppStringFromObjCString(serviceType)); - // Set TXTRecord converted from NSDictionary to hash map. - if (txtRecordData != nil) { - auto txt_records = AbslHashMapFromObjCTxtRecords(txtRecordData); - nsd_service_info.SetTxtRecords(txt_records); - } - connection_requesters_.insert({CppStringFromObjCString(serviceType), requestConnection}); + + NSError* error = nil; + BOOL result = [medium_ startDiscoveryForServiceType:serviceType + serviceFoundHandler:^(NSString* name, NSDictionary* txtRecords) { + NsdServiceInfo nsd_service_info; + nsd_service_info.SetServiceType([serviceType UTF8String]); + nsd_service_info.SetServiceName([name UTF8String]); + [txtRecords + enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info]( + NSString* key, NSString* val, BOOL* stop) { + nsd_service_info->SetTxtRecord([key UTF8String], [val UTF8String]); + }]; client_callback.service_discovered_cb(nsd_service_info); - return ^{ - client_callback.service_lost_cb(nsd_service_info); - }; - }]; - { - absl::MutexLock lock(&mutex_); - discovering_info_.Add(service_type, browser); + } + serviceLostHandler:^(NSString* name, NSDictionary* txtRecords) { + NsdServiceInfo nsd_service_info; + nsd_service_info.SetServiceType([serviceType UTF8String]); + nsd_service_info.SetServiceName([name UTF8String]); + [txtRecords + enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info]( + NSString* key, NSString* val, BOOL* stop) { + nsd_service_info->SetTxtRecord([key UTF8String], [val UTF8String]); + }]; + client_callback.service_lost_cb(nsd_service_info); + } + error:&error]; + if (error != nil) { + GTMLoggerError(@"Error starting discovery for service type<%@>: %@", serviceType, error); } - return true; + return result; } bool WifiLanMedium::StopDiscovery(const std::string& service_type) { - { - absl::MutexLock lock(&mutex_); - if (!discovering_info_.Existed(service_type)) { - GTMLoggerInfo(@"[NEARBY] WifiLan StopDiscovery: Can't stop discovering because " - "we never started discovering."); - return false; - } - discovering_info_.Remove(service_type); - } + NSString* serviceType = @(service_type.c_str()); + [medium_ stopDiscoveryForServiceType:serviceType]; return true; } std::unique_ptr WifiLanMedium::ConnectToService( const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) { - std::string service_type = remote_service_info.GetServiceType(); - GTMLoggerInfo(@"[NEARBY] WifiLan ConnectToService, service_type=%@", - ObjCStringFromCppString(service_type)); - - GNCMConnectionRequester connection_requester = nil; - { - absl::MutexLock lock(&mutex_); - const auto& it = connection_requesters_.find(service_type); - if (it == connection_requesters_.end()) { - return {}; - } - connection_requester = it->second; + NSError* error = nil; + NSString* serviceName = @(remote_service_info.GetServiceName().c_str()); + NSString* serviceType = @(remote_service_info.GetServiceType().c_str()); + GNCWiFiLANSocket* socket = [medium_ connectToServiceName:serviceName + serviceType:serviceType + error:&error]; + if (socket != nil) { + return std::make_unique(socket); } - - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); - __block std::unique_ptr socket; - if (connection_requester != nil) { - if (cancellation_flag->Cancelled()) { - GTMLoggerError(@"[NEARBY] WifiLan Connect: Has been cancelled: service_type=%@", - ObjCStringFromCppString(service_type)); - dispatch_group_leave(group); // unblock - return {}; - } - - connection_requester(^(id connection) { - // If the connection wasn't successfully established, return a NULL socket. - if (connection) { - socket = absl::make_unique(connection); - } - - dispatch_group_leave(group); // unblock - return socket != nullptr ? static_cast(socket->GetInputStream()) - .GetConnectionHandlers() - : nullptr; - }); + if (error != nil) { + GTMLoggerError(@"Error connecting to service name<%@> type<%@>: %@", serviceName, serviceType, + error); } - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - return std::move(socket); + return nil; } std::unique_ptr WifiLanMedium::ConnectToService( const std::string& ip_address, int port, CancellationFlag* cancellation_flag) { - // Not implemented. - return {}; + NSError* error = nil; + NSString* host = @(ip_address.c_str()); + GNCWiFiLANSocket* socket = [medium_ connectToHost:host port:port error:&error]; + if (socket != nil) { + return std::make_unique(socket); + } + if (error != nil) { + GTMLoggerError(@"Error connecting to %@:%d: %@", host, port, error); + } + return nil; } std::unique_ptr WifiLanMedium::ListenForService(int port) { - auto server_socket = std::make_unique(); - // The fake ip address and port need to be set here since they can't be retrieved before - // StartAadvertising begins. Furthermore, NSNetService can't resolve ip address when finding - // service. Try to fake them and make it socket name as a key of server_sockets_ which acts - // the same socket binding. - server_socket->SetIPAddress(GetFakeIPAddress()); - requesting_port_ = port; - server_socket->SetPort(requesting_port_ == 0 ? GetFakePort() : requesting_port_); - std::string socket_name = - WifiLanServerSocket::GetName(server_socket->GetIPAddress(), server_socket->GetPort()); - server_socket->SetCloseNotifier([this, socket_name]() { - absl::MutexLock lock(&mutex_); - server_sockets_.erase(socket_name); - }); - GTMLoggerInfo(@"[NEARBY] WifiLan Adding server socket, socket_name=%@", - ObjCStringFromCppString(socket_name)); - absl::MutexLock lock(&mutex_); - server_sockets_.insert({socket_name, server_socket.get()}); - return server_socket; -} - -std::string WifiLanMedium::GetFakeIPAddress() const { - std::string ip_address; - ip_address.resize(4); - uint32_t raw_ip_addr = Prng().NextUint32(); - ip_address[0] = static_cast(raw_ip_addr >> 24); - ip_address[1] = static_cast(raw_ip_addr >> 16); - ip_address[2] = static_cast(raw_ip_addr >> 8); - ip_address[3] = static_cast(raw_ip_addr >> 0); - - return ip_address; -} - -int WifiLanMedium::GetFakePort() const { - uint16_t port = Prng().NextUint32(); - - return port; + NSError* error = nil; + GNCWiFiLANServerSocket* serverSocket = [medium_ listenForServiceOnPort:port error:&error]; + if (serverSocket != nil) { + return std::make_unique(serverSocket); + } + if (error != nil) { + GTMLoggerError(@"Error listening for service: %@", error); + } + return nil; } } // namespace apple