diff --git a/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h b/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h index 355569ce..ee0014ea 100644 --- a/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h +++ b/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import // NOLINT +#import // NOLINT #import +NS_ASSUME_NONNULL_BEGIN + @class GNCIPv4Address; @class GNCHotspotSocket; @@ -26,6 +28,14 @@ */ @interface GNCHotspotMedium : NSObject +/** + * Initializes the Hotspot medium with the specified queue. + * + * @param queue The queue to use for all internal operations. + * @return An initialized Hotspot medium. + */ +- (instancetype)initWithQueue:(dispatch_queue_t)queue; + /** * Connects to a Wifi Hotspot with the specified SSID and password. * @@ -33,15 +43,14 @@ * @param password The password of the Hotspot to connect to. * @return YES if the connection was successful, NO otherwise. */ -- (BOOL)connectToWifiNetworkWithSSID:(nonnull NSString *)ssid - password:(nonnull NSString *)password; +- (BOOL)connectToWifiNetworkWithSSID:(NSString *)ssid password:(NSString *)password; /** * Disconnects from a Wifi Hotspot with the specified SSID. * * @param ssid The SSID of the Hotspot to disconnect from. */ -- (void) disconnectToWifiNetworkWithSSID:(nonnull NSString *)ssid; +- (void)disconnectToWifiNetworkWithSSID:(NSString *)ssid; /** * Gets the current Wifi SSID. @@ -58,8 +67,11 @@ * @param[out] error Error that will be populated on failure. * @return Returns a connected socket or nil if an error has occurred. */ -- (nullable GNCHotspotSocket *)connectToHost:(nonnull GNCIPv4Address *)host - port:(NSInteger)port - error:(NSError **_Nullable)error; +- (nullable GNCHotspotSocket *)connectToHost:(GNCIPv4Address *)host + port:(NSInteger)port + cancelSource:(nullable dispatch_source_t)cancelSource + error:(NSError *_Nullable *_Nullable)error; @end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.m b/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.m index 37ccc743..b9750f3b 100644 --- a/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.m +++ b/internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.m @@ -27,6 +27,8 @@ #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h" +NS_ASSUME_NONNULL_BEGIN + #if TARGET_OS_IOS // The maximum number of retries for connecting to the Hotspot. static const UInt8 kMaxRetryCount = 3; @@ -38,10 +40,23 @@ static const UInt8 kConnectionTimeoutInSeconds = 18; // An arbitrary timeout that should be pretty lenient. static const UInt8 kConnectionToHostTimeoutInSeconds = 10; -@implementation GNCHotspotMedium +@implementation GNCHotspotMedium { + dispatch_queue_t _hotspot_queue; +} -- (BOOL)connectToWifiNetworkWithSSID:(nonnull NSString *)ssid - password:(nonnull NSString *)password { +- (instancetype)init { + return [self initWithQueue:dispatch_get_main_queue()]; +} + +- (instancetype)initWithQueue:(dispatch_queue_t)queue { + self = [super init]; + if (self) { + _hotspot_queue = queue; + } + return self; +} + +- (BOOL)connectToWifiNetworkWithSSID:(NSString *)ssid password:(NSString *)password { #if TARGET_OS_IOS __block BOOL connected = NO; NEHotspotConfiguration *config = [[NEHotspotConfiguration alloc] initWithSSID:ssid @@ -98,7 +113,7 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; #endif // TARGET_OS_IOS } -- (void)disconnectToWifiNetworkWithSSID:(nonnull NSString *)ssid { +- (void)disconnectToWifiNetworkWithSSID:(NSString *)ssid { #if TARGET_OS_IOS [[NEHotspotConfigurationManager sharedManager] removeConfigurationForSSID:ssid]; #else @@ -106,9 +121,10 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; #endif // TARGET_OS_IOS } -- (GNCHotspotSocket *)connectToHost:(GNCIPv4Address *)host - port:(NSInteger)port - error:(NSError **)error { +- (nullable GNCHotspotSocket *)connectToHost:(GNCIPv4Address *)host + port:(NSInteger)port + cancelSource:(nullable dispatch_source_t)cancelSource + error:(NSError *_Nullable *_Nullable)error { // Validate host address if (!host.dottedRepresentation.UTF8String) { if (error) { @@ -122,12 +138,16 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; nw_endpoint_t endpoint = nw_endpoint_create_host(host.dottedRepresentation.UTF8String, @(port).stringValue.UTF8String); - return [self connectToEndpoint:endpoint includePeerToPeer:(BOOL)YES error:error]; + return [self connectToEndpoint:endpoint + includePeerToPeer:(BOOL)YES + cancelSource:cancelSource + error:error]; } -- (GNCHotspotSocket *)connectToEndpoint:(nw_endpoint_t)endpoint - includePeerToPeer:(BOOL)includePeerToPeer - error:(NSError **)error { +- (nullable GNCHotspotSocket *)connectToEndpoint:(nw_endpoint_t)endpoint + includePeerToPeer:(BOOL)includePeerToPeer + cancelSource:(nullable dispatch_source_t)cancelSource + error:(NSError *_Nullable *_Nullable)error { GNCLoggerInfo(@"connectToEndpoint: %@ includePeerToPeer: %d", endpoint.debugDescription, includePeerToPeer); @@ -142,7 +162,7 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; nw_parameters_set_include_peer_to_peer(parameters, includePeerToPeer); nw_connection_t connection = nw_connection_create(endpoint, parameters); - nw_connection_set_queue(connection, dispatch_get_main_queue()); + nw_connection_set_queue(connection, _hotspot_queue); nw_connection_set_state_changed_handler( connection, ^(nw_connection_state_t state, nw_error_t error) { @@ -159,6 +179,14 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; } }); + if (cancelSource != nil) { + dispatch_source_set_event_handler(cancelSource, ^{ + GNCLoggerWarning(@"connectToEndpoint is cancelled."); + nw_connection_cancel(connection); + dispatch_source_cancel(cancelSource); + }); + } + nw_connection_start(connection); dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kConnectionToHostTimeoutInSeconds * NSEC_PER_SEC); @@ -249,3 +277,5 @@ static const UInt8 kConnectionToHostTimeoutInSeconds = 10; } @end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/wifi_hotspot.h b/internal/platform/implementation/apple/wifi_hotspot.h index 7795d68c..62125029 100644 --- a/internal/platform/implementation/apple/wifi_hotspot.h +++ b/internal/platform/implementation/apple/wifi_hotspot.h @@ -138,6 +138,7 @@ class WifiHotspotMedium : public api::WifiHotspotMedium { } private: + dispatch_queue_t hotspot_queue_ = nil; GNCHotspotMedium* medium_; NSString* hotspot_ssid_ = nil; }; diff --git a/internal/platform/implementation/apple/wifi_hotspot.mm b/internal/platform/implementation/apple/wifi_hotspot.mm index 99bd0ce4..7dfb14f1 100644 --- a/internal/platform/implementation/apple/wifi_hotspot.mm +++ b/internal/platform/implementation/apple/wifi_hotspot.mm @@ -21,6 +21,7 @@ #include #include +#include "internal/platform/cancellation_flag_listener.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h" #import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotSocket.h" @@ -28,6 +29,9 @@ namespace nearby { namespace apple { +namespace { +constexpr char kHotspotQueueLabel[] = "com.google.nearby.wifi_hotspot"; +} #pragma mark - WifiHotspotInputStream @@ -93,7 +97,11 @@ Exception WifiHotspotSocket::Close() { #pragma mark - WifiHotspotMedium -WifiHotspotMedium::WifiHotspotMedium() : medium_([[GNCHotspotMedium alloc] init]) {} // NOLINT +WifiHotspotMedium::WifiHotspotMedium() { + hotspot_queue_ = dispatch_queue_create(kHotspotQueueLabel, DISPATCH_QUEUE_SERIAL); + medium_ = [[GNCHotspotMedium alloc] initWithQueue:hotspot_queue_]; +} + WifiHotspotMedium::~WifiHotspotMedium() { DisconnectWifiHotspot(); } bool WifiHotspotMedium::ConnectWifiHotspot(HotspotCredentials* hotspot_credentials_) { @@ -146,7 +154,29 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( } GNCLoggerInfo(@"Connect to Hotspot host server: %@", [host dottedRepresentation]); - GNCHotspotSocket* socket = [medium_ connectToHost:host port:port error:&error]; + // Setup cancel listener + std::unique_ptr connection_cancellation_listener = nullptr; + dispatch_source_t cancellation_source = nullptr; + if (cancellation_flag != nullptr) { + if (cancellation_flag->Cancelled()) { + return nullptr; + } + + cancellation_source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, hotspot_queue_); + + dispatch_resume(cancellation_source); + connection_cancellation_listener = std::make_unique( + cancellation_flag, [&cancellation_source]() { + GNCLoggerWarning(@"Cancelling to connect WiFi hotspot."); + dispatch_source_merge_data(cancellation_source, 1); + }); + } + + GNCHotspotSocket* socket = [medium_ connectToHost:host + port:port + cancelSource:cancellation_source + error:&error]; if (socket != nil) { return std::make_unique(socket); }