Add cancel support for hotspot connection

PiperOrigin-RevId: 791394599
This commit is contained in:
Guogang Li
2025-08-05 15:33:33 -07:00
committed by Copybara-Service
parent e66394d4f7
commit 0640a9c13c
4 changed files with 94 additions and 21 deletions
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h> // NOLINT
#import <Foundation/Foundation.h> // NOLINT
#import <Network/Network.h>
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
@@ -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
@@ -138,6 +138,7 @@ class WifiHotspotMedium : public api::WifiHotspotMedium {
}
private:
dispatch_queue_t hotspot_queue_ = nil;
GNCHotspotMedium* medium_;
NSString* hotspot_ssid_ = nil;
};
@@ -21,6 +21,7 @@
#include <utility>
#include <arpa/inet.h>
#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<api::WifiHotspotSocket> 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<nearby::CancellationFlagListener> 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<nearby::CancellationFlagListener>(
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<WifiHotspotSocket>(socket);
}