Update the Apple Wi-Fi LAN medium to use WifiLanV2

PiperOrigin-RevId: 510228266
This commit is contained in:
Nick Bourdakos
2023-02-16 13:28:38 -08:00
committed by Copybara-Service
parent 949d882727
commit 2c47104dc9
3 changed files with 183 additions and 518 deletions
+1 -5
View File
@@ -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",
],
+55 -121
View File
@@ -16,185 +16,119 @@
#define PLATFORM_IMPL_APPLE_WIFI_LAN_H_
#import <Foundation/Foundation.h>
#import <Network/Network.h>
#include <string>
#include <utility>
#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<ByteArray> Read(std::int64_t size) override;
Exception Close() override;
GNCMConnectionHandlers* GetConnectionHandlers() { return connectionHandlers_; }
private:
GNCMConnectionHandlers* connectionHandlers_;
NSMutableArray<NSData*>* 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<GNCMConnection> 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<GNCMConnection> connection_;
NSCondition* condition_;
GNCWiFiLANSocket* socket_;
};
/** Concrete WifiLanSocket implementation. */
/**
* Concrete WifiLanSocket implementation.
*/
class WifiLanSocket : public api::WifiLanSocket {
public:
WifiLanSocket() = default;
explicit WifiLanSocket(id<GNCMConnection> 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<WifiLanInputStream> input_stream_;
std::unique_ptr<WifiLanOutputStream> 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<api::WifiLanSocket> Accept() override ABSL_LOCKS_EXCLUDED(mutex_);
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
bool Connect(std::unique_ptr<WifiLanSocket> socket) ABSL_LOCKS_EXCLUDED(mutex_);
void SetCloseNotifier(absl::AnyInvocable<void()> notifier) ABSL_LOCKS_EXCLUDED(mutex_);
std::string GetIPAddress() const override;
int GetPort() const override;
std::unique_ptr<api::WifiLanSocket> 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<std::unique_ptr<WifiLanSocket>> pending_sockets_ ABSL_GUARDED_BY(mutex_);
absl::AnyInvocable<void()> 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<api::WifiLanSocket> ConnectToService(
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port) override
ABSL_LOCKS_EXCLUDED(mutex_);
absl::optional<std::pair<std::int32_t, std::int32_t>> 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<api::WifiLanSocket> ConnectToService(
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanServerSocket> 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<std::string, GNCMBonjourService*> 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<std::string, GNCMBonjourBrowser*> 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<std::string, WifiLanServerSocket*> server_sockets_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<std::string, GNCMConnectionRequester> connection_requesters_
ABSL_GUARDED_BY(mutex_);
GNCWiFiLANMedium* medium_;
absl::AnyInvocable<void(NsdServiceInfo)> service_discovered_cb_;
absl::AnyInvocable<void(NsdServiceInfo)> service_lost_cb_;
};
} // namespace apple
+127 -392
View File
@@ -18,478 +18,213 @@
#include <string>
#include <utility>
#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<ByteArray> 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<ByteArray>(ByteArrayFromNSData(dataToReturn));
} else {
return ExceptionOr<ByteArray>{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>{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<GNCMConnection> 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<WifiLanInputStream>(socket)),
output_stream_(std::make_unique<WifiLanOutputStream>(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<api::WifiLanSocket> 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<WifiLanSocket>(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<WifiLanSocket> 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<void()> 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<NSString*, NSData*>* 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<GNCMConnection> 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<WifiLanSocket>(connection);
GNCMConnectionHandlers* connectionHandlers =
static_cast<WifiLanInputStream&>(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<NSString*, NSString*>* 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<NSString*, NSData*>* _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<NSString*, NSString*>* 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<NSString*, NSString*>* 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<api::WifiLanSocket> 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<WifiLanSocket>(socket);
}
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block std::unique_ptr<WifiLanSocket> 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<GNCMConnection> connection) {
// If the connection wasn't successfully established, return a NULL socket.
if (connection) {
socket = absl::make_unique<WifiLanSocket>(connection);
}
dispatch_group_leave(group); // unblock
return socket != nullptr ? static_cast<WifiLanInputStream&>(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<api::WifiLanSocket> 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<WifiLanSocket>(socket);
}
if (error != nil) {
GTMLoggerError(@"Error connecting to %@:%d: %@", host, port, error);
}
return nil;
}
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(int port) {
auto server_socket = std::make_unique<WifiLanServerSocket>();
// 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<char>(raw_ip_addr >> 24);
ip_address[1] = static_cast<char>(raw_ip_addr >> 16);
ip_address[2] = static_cast<char>(raw_ip_addr >> 8);
ip_address[3] = static_cast<char>(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<WifiLanServerSocket>(serverSocket);
}
if (error != nil) {
GTMLoggerError(@"Error listening for service: %@", error);
}
return nil;
}
} // namespace apple