mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Update the Apple Wi-Fi LAN medium to use WifiLanV2
PiperOrigin-RevId: 510228266
This commit is contained in:
committed by
Copybara-Service
parent
949d882727
commit
2c47104dc9
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user