mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
ios: nearbyConnections: Move source to //third_party.
PiperOrigin-RevId: 408046875
This commit is contained in:
committed by
Copybara-Service
parent
8c2dd35eac
commit
fb0337ebfa
@@ -0,0 +1,57 @@
|
||||
load("//tools/build_defs/apple:objc.bzl", "objc_proto_library")
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//platform/impl/ios:__subpackages__"])
|
||||
|
||||
objc_library(
|
||||
name = "Mediums",
|
||||
srcs = [
|
||||
"GNCLeaks.m",
|
||||
"GNCMConnection.m",
|
||||
"WifiLan/GNCMBonjourBrowser.m",
|
||||
"WifiLan/GNCMBonjourConnection.m",
|
||||
"WifiLan/GNCMBonjourService.m",
|
||||
"WifiLan/GNCMBonjourUtils.m",
|
||||
],
|
||||
hdrs = [
|
||||
"GNCLeaks.h",
|
||||
"GNCMConnection.h",
|
||||
"WifiLan/GNCMBonjourBrowser.h",
|
||||
"WifiLan/GNCMBonjourConnection.h",
|
||||
"WifiLan/GNCMBonjourService.h",
|
||||
"WifiLan/GNCMBonjourUtils.h",
|
||||
],
|
||||
deps = [
|
||||
":ObjCProtos",
|
||||
"//base",
|
||||
"//absl/numeric:int128",
|
||||
"//platform/impl/ios/Source/Shared",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
|
||||
objc_proto_library(
|
||||
name = "ObjCProtos",
|
||||
deps = [":Protos"],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "Protos",
|
||||
deps = [
|
||||
"//proto/connections:offline_wire_formats_proto",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Verifies that an object has been deallocated after the given time period.
|
||||
void GNCVerifyDealloc(id object, NSTimeInterval timeInterval);
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCLeaks.h"
|
||||
|
||||
void GNCVerifyDealloc(id object, NSTimeInterval timeInterval) {
|
||||
#if DEBUG
|
||||
__weak id weakObj = object;
|
||||
NSCAssert(weakObj != nil, @"Pointer to %@ is already nil", weakObj);
|
||||
NSLog(@"Verifying deallocation of %@", NSStringFromClass([weakObj class]));
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInterval * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), ^{
|
||||
NSCAssert(weakObj == nil, @"%@ not deallocated.", weakObj);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** Result of a medium payload transfer. */
|
||||
typedef NS_ENUM(NSInteger, GNCMPayloadResult) {
|
||||
GNCMPayloadResultSuccess, // Payload delivery was successful.
|
||||
GNCMPayloadResultFailure, // An error occurred during payload delivery.
|
||||
GNCMPayloadResultCanceled, // Payload delivery was canceled.
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCMPayloadResult value. */
|
||||
typedef void (^GNCMPayloadResultHandler)(GNCMPayloadResult);
|
||||
|
||||
/**
|
||||
* A progress handler is periodically called during payload delivery. It is passed a value
|
||||
* ranging from 0 (when the operation has just started) to the total size (when the operation is
|
||||
* finished).
|
||||
*/
|
||||
typedef void (^GNCMProgressHandler)(size_t count);
|
||||
|
||||
/** This handler is called when data is received from a remote endpoint. */
|
||||
typedef void (^GNCMPayloadHandler)(NSData *data);
|
||||
|
||||
/**
|
||||
* This represents a connection with a remote endpoint at the medium level. Use it to send
|
||||
* payloads to the remote endpoint, and release it to disconnect.
|
||||
*/
|
||||
@protocol GNCMConnection <NSObject>
|
||||
|
||||
/**
|
||||
* Sends data to the remote endpoint. Wait for the completion to be called before sending another
|
||||
* payload.
|
||||
*
|
||||
* @param payload The data to send.
|
||||
* @param progressHandler Called repeatedly for progress feedback while the data is being sent.
|
||||
* @param completion Callback called when the data has been fully sent,
|
||||
* or it has failed to be sent (not connected or disconnected).
|
||||
*/
|
||||
- (void)sendData:(NSData *)payload
|
||||
progressHandler:(GNCMProgressHandler)progressHandler
|
||||
completion:(GNCMPayloadResultHandler)completion;
|
||||
|
||||
@end
|
||||
|
||||
/** This class contains optional handlers for a connection. */
|
||||
@interface GNCMConnectionHandlers : NSObject
|
||||
|
||||
/** This handler is called when data is sent from the remote endpoint. */
|
||||
@property(nonatomic) GNCMPayloadHandler payloadHandler;
|
||||
|
||||
/** This handler is called when the connection is ended. */
|
||||
@property(nonatomic) dispatch_block_t disconnectedHandler;
|
||||
|
||||
/** This method creates a GNCMConnectionHandlers object from payload and disconnect handlers. */
|
||||
+ (instancetype)payloadHandler:(GNCMPayloadHandler)payloadHandler
|
||||
disconnectedHandler:(dispatch_block_t)disconnectedHandler;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler takes a GNCMConnection object and returns a GNCMConnectionHandlers object. It is
|
||||
* called when a connection is successfully made with a remote endpoint. If |connection| is nil,
|
||||
* the connection couldn't be established; in this case, return nil.
|
||||
*/
|
||||
typedef GNCMConnectionHandlers *_Nullable (^GNCMConnectionHandler)(
|
||||
id<GNCMConnection> __nullable connection);
|
||||
|
||||
/**
|
||||
* This handler is called by a discovering endpoint to request a connection with an an advertising
|
||||
* endpoint.
|
||||
*/
|
||||
typedef void (^GNCMConnectionRequester)(GNCMConnectionHandler connectionHandler);
|
||||
|
||||
/** This handler is called when a previously discovered advertising endpoint is lost. */
|
||||
typedef void (^GNCMEndpointLostHandler)(void);
|
||||
|
||||
/**
|
||||
* This handler is called on a discoverer when a nearby advertising endpoint is
|
||||
* discovered. Calls |requestConnection| to request a connection with the advertiser.
|
||||
*/
|
||||
typedef GNCMEndpointLostHandler _Nonnull (^GNCMEndpointFoundHandler)(
|
||||
NSString *endpointId, NSString *serviceType, NSString *serviceName,
|
||||
NSDictionary<NSString *, NSData *> *_Nullable TXTRecordData,
|
||||
GNCMConnectionRequester requestConnection);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCMConnectionHandlers
|
||||
|
||||
+ (instancetype)payloadHandler:(GNCMPayloadHandler)payloadHandler
|
||||
disconnectedHandler:(dispatch_block_t)disconnectedHandler {
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = payloadHandler;
|
||||
handlers.disconnectedHandler = disconnectedHandler;
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GNCMBonjourBrowser browses mDNS services publishing the specified mDNS type and domain. The mDNS
|
||||
* type is a string formatted as "_[serviceIdHash]._tcp." in which [serviceIdHash] is generated as
|
||||
* a SHA-256 hash from the service ID and taken the 6 first bytes of string in upper case.
|
||||
* Calls the specififed endpoint found handler when one is found. The endpoint found handler
|
||||
* supplies a requester block, which can be called to establish a socket to the service found.
|
||||
*
|
||||
* Don't hold the strong reference of caller self in endpointFoundHandler to ensure there is no
|
||||
* retain cycle between them.
|
||||
*
|
||||
* @param serviceType An mDNS type that uniquely identifies the published service to search for.
|
||||
* @param endpointFoundHandler The handler that is called when an endpoint publishing the service
|
||||
* ID is discovered.
|
||||
*/
|
||||
@interface GNCMBonjourBrowser : NSObject
|
||||
|
||||
- (instancetype)initWithServiceType:(NSString *)serviceType
|
||||
endpointFoundHandler:(GNCMEndpointFoundHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,176 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h"
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
typedef NSString *GNCEndpointId;
|
||||
|
||||
@interface GNCMNetServiceInfo : NSObject
|
||||
@property(nonatomic) NSNetService *service;
|
||||
@property(nonatomic, copy) GNCMEndpointLostHandler endpointLostHandler;
|
||||
@property(nonatomic, copy, nullable) GNCMConnectionHandler connectionHandler;
|
||||
@end
|
||||
|
||||
@implementation GNCMNetServiceInfo
|
||||
|
||||
+ (instancetype)infoWithService:(NSNetService *)service {
|
||||
GNCMNetServiceInfo *info = [[GNCMNetServiceInfo alloc] init];
|
||||
info.service = service;
|
||||
return info;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(GNCMNetServiceInfo *)object {
|
||||
return [_service isEqual:object.service];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [_service hash];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GNCMBonjourBrowser () <NSNetServiceBrowserDelegate, NSNetServiceDelegate>
|
||||
|
||||
@property(nonatomic, copy) GNCMEndpointFoundHandler endpointFoundHandler;
|
||||
|
||||
@property(nonatomic) NSNetServiceBrowser *netBrowser;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, GNCMNetServiceInfo *> *endpoints;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourBrowser
|
||||
|
||||
- (instancetype)initWithServiceType:(NSString *)serviceType
|
||||
endpointFoundHandler:(GNCMEndpointFoundHandler)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_endpointFoundHandler = handler;
|
||||
|
||||
_netBrowser = [[NSNetServiceBrowser alloc] init];
|
||||
_netBrowser.delegate = self;
|
||||
[_netBrowser scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_netBrowser searchForServicesOfType:serviceType inDomain:GNCMBonjourDomain];
|
||||
_endpoints = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceBrowserDelegate
|
||||
|
||||
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser {
|
||||
GTMLoggerDebug(@"Browsing");
|
||||
}
|
||||
|
||||
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser {
|
||||
GTMLoggerDebug(@"Stop browsing");
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didNotSearch:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Not browsing with errorDict: %@", errorDict);
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didFindService:(NSNetService *)service
|
||||
moreComing:(BOOL)moreComing {
|
||||
GTMLoggerDebug(@"Found service: %@", service);
|
||||
|
||||
GNCMNetServiceInfo *info = [GNCMNetServiceInfo infoWithService:service];
|
||||
|
||||
// Just to be safe, check if the service is already known and deal with it accordingly.
|
||||
NSArray<GNCEndpointId> *endpointIds = [_endpoints allKeysForObject:info];
|
||||
if (endpointIds.count > 0) return;
|
||||
|
||||
// Add the newly discovered service to the list of services.
|
||||
GNCEndpointId endpointId = [[NSUUID UUID] UUIDString];
|
||||
_endpoints[endpointId] = info;
|
||||
|
||||
service.delegate = self;
|
||||
[service resolveWithTimeout:0];
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didRemoveService:(NSNetService *)service
|
||||
moreComing:(BOOL)moreComing {
|
||||
GTMLoggerDebug(@"Lost service: %@", service);
|
||||
NSArray<GNCEndpointId> *endpointIds =
|
||||
[_endpoints allKeysForObject:[GNCMNetServiceInfo infoWithService:service]];
|
||||
NSAssert(endpointIds.count <= 1, @"Unexpected duplicate service");
|
||||
if (endpointIds.count > 0) {
|
||||
GNCEndpointId endpointId = endpointIds[0];
|
||||
GNCMNetServiceInfo *info = _endpoints[endpointId];
|
||||
[_endpoints removeObjectForKey:endpointId];
|
||||
// Tail call to preserve reentrancy.
|
||||
if (info.endpointLostHandler) {
|
||||
info.endpointLostHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceDelegate
|
||||
|
||||
- (void)netServiceDidResolveAddress:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Resolved service: %@ addresses: %@", service, service.addresses);
|
||||
|
||||
GNCMNetServiceInfo *info = [GNCMNetServiceInfo infoWithService:service];
|
||||
|
||||
NSArray<GNCEndpointId> *endpointIds = [_endpoints allKeysForObject:info];
|
||||
if (endpointIds.count > 0) {
|
||||
// Get TXTRecord data.
|
||||
NSData *data = [service TXTRecordData];
|
||||
NSDictionary<NSString *, NSData *> *TXTRecordData =
|
||||
[NSNetService dictionaryFromTXTRecordData:data];
|
||||
|
||||
// The endpointLostHandler is returned from the endpointFoundHandler. The main benefit of this
|
||||
// is that it allows rejection from the remote endpoint to be received by the local endpoint
|
||||
// before the local endpoint has accepted or rejected.
|
||||
info.endpointLostHandler = _endpointFoundHandler(
|
||||
endpointIds[0], service.type, service.name, TXTRecordData,
|
||||
^(GNCMConnectionHandler connectionHandler) {
|
||||
// A connection is requested, so resolve the service to get the I/O streams.
|
||||
info.connectionHandler = connectionHandler;
|
||||
if (info.connectionHandler) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
NSInputStream *inputStream;
|
||||
NSOutputStream *outputStream;
|
||||
[info.service scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[info.service getInputStream:&inputStream outputStream:&outputStream];
|
||||
GNCMBonjourConnection *connection =
|
||||
[[GNCMBonjourConnection alloc] initWithInputStream:inputStream
|
||||
outputStream:outputStream
|
||||
queue:nil];
|
||||
connection.connectionHandlers = info.connectionHandler(connection);
|
||||
});
|
||||
}
|
||||
});
|
||||
_endpoints[endpointIds[0]] = info;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didNotResolve:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Did not resolve service: %@", service);
|
||||
NSArray<GNCEndpointId> *endpointIds =
|
||||
[_endpoints allKeysForObject:[GNCMNetServiceInfo infoWithService:service]];
|
||||
if (endpointIds.count > 0) {
|
||||
_endpoints[endpointIds[0]].connectionHandler = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* This medium connection sends and receives payloads over the NSInputStream and NSOutputStream
|
||||
* passed to it.
|
||||
*
|
||||
* @param inputStream The input stream to read from.
|
||||
* @param outputStream The output stream to write to.
|
||||
* @param queue The queue on which the GNCMConnection callbacks will be called. If nil, the main
|
||||
* queue is used.
|
||||
*/
|
||||
@interface GNCMBonjourConnection : NSObject <GNCMConnection>
|
||||
@property(nonatomic) GNCMConnectionHandlers *connectionHandlers;
|
||||
|
||||
- (instancetype)initWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
enum { kMaxPacketSize = 32 * 1024 };
|
||||
|
||||
@interface GNCMBonjourConnection () <NSStreamDelegate>
|
||||
@property(nonatomic) NSInputStream *inputStream;
|
||||
@property(nonatomic) NSOutputStream *outputStream;
|
||||
@property(nonatomic) dispatch_queue_t callbackQueue;
|
||||
|
||||
@property(nonatomic, copy, nullable) NSData *dataBeingWritten;
|
||||
@property(nonatomic) NSInteger numberOfBytesLeftToWrite;
|
||||
@property(nonatomic, copy, nullable) GNCMProgressHandler progressHandler;
|
||||
@property(nonatomic, copy, nullable) GNCMPayloadResultHandler completion;
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourConnection
|
||||
|
||||
- (instancetype)initWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream
|
||||
queue:(nullable dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_inputStream = inputStream;
|
||||
_inputStream.delegate = self;
|
||||
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_inputStream open];
|
||||
|
||||
_outputStream = outputStream;
|
||||
_outputStream.delegate = self;
|
||||
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_outputStream open];
|
||||
|
||||
_callbackQueue = queue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self closeStreams];
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)payload
|
||||
progressHandler:(GNCMProgressHandler)progressHandler
|
||||
completion:(GNCMPayloadResultHandler)completion {
|
||||
if (_dataBeingWritten) {
|
||||
GTMLoggerInfo(@"Attempting to send payload while one is already in flight");
|
||||
[self dispatchCallback:^{
|
||||
completion(GNCMPayloadResultFailure);
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
self.dataBeingWritten = payload;
|
||||
self.numberOfBytesLeftToWrite = payload.length;
|
||||
self.progressHandler = progressHandler;
|
||||
self.completion = completion;
|
||||
[self writeChunk];
|
||||
}
|
||||
|
||||
#pragma mark NSStreamDelegate
|
||||
|
||||
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
|
||||
switch (event) {
|
||||
case NSStreamEventHasBytesAvailable: {
|
||||
// Data has arrived on the input stream.
|
||||
NSAssert(stream == _inputStream, @"Error: Expected input stream");
|
||||
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
uint8_t bytesRead[kMaxPacketSize];
|
||||
NSInteger numberOfBytesRead;
|
||||
@synchronized(self.inputStream) {
|
||||
numberOfBytesRead = [self.inputStream read:bytesRead maxLength:kMaxPacketSize];
|
||||
}
|
||||
NSData *data = nil;
|
||||
if (numberOfBytesRead > 0) {
|
||||
GTMLoggerInfo(@"Read %lu bytes", (u_long)numberOfBytesRead);
|
||||
data = [NSData dataWithBytes:bytesRead length:numberOfBytesRead];
|
||||
}
|
||||
[self dispatchCallback:^{
|
||||
self.connectionHandlers.payloadHandler(data ?: [NSData data]);
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
// There is space available on the output stream.
|
||||
NSAssert(stream == _outputStream, @"Error: Expected output stream");
|
||||
|
||||
// Schedule this in a future runloop cycle because -writeChunk, which can cause this event
|
||||
// to be received synchronously, is not reentrant.
|
||||
[self performSelector:@selector(writeChunk) withObject:nil afterDelay:0.0];
|
||||
break;
|
||||
|
||||
case NSStreamEventErrorOccurred:
|
||||
GTMLoggerInfo(@"Stream error: %@", [stream streamError]);
|
||||
// Fall through.
|
||||
case NSStreamEventEndEncountered: {
|
||||
GTMLoggerInfo(@"Stream closing");
|
||||
[self closeStreams];
|
||||
if (_connectionHandlers.disconnectedHandler) {
|
||||
[self dispatchCallback:^{
|
||||
self.connectionHandlers.disconnectedHandler();
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventOpenCompleted:
|
||||
case NSStreamEventNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
// Calls a block on the callback queue.
|
||||
- (void)dispatchCallback:(dispatch_block_t)block {
|
||||
dispatch_async(_callbackQueue ?: dispatch_get_main_queue(), block);
|
||||
}
|
||||
|
||||
// Writes a chunk of the outgoing data to the output stream, calling the progress and completion
|
||||
// handlers as needed.
|
||||
- (void)writeChunk {
|
||||
void (^reportProgress)(size_t) = ^(size_t count) {
|
||||
// Captures the progress handler because the property is nilled out below.
|
||||
GNCMProgressHandler progressHandler = _progressHandler;
|
||||
if (progressHandler != nil) {
|
||||
[self dispatchCallback:^{
|
||||
progressHandler(count);
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
void (^completed)(GNCMPayloadResult) = ^(GNCMPayloadResult result) {
|
||||
reportProgress(_dataBeingWritten.length);
|
||||
_progressHandler = nil;
|
||||
_dataBeingWritten = nil;
|
||||
|
||||
// Captures the completion because the property is nilled out below.
|
||||
GNCMPayloadResultHandler completion = _completion;
|
||||
if (completion != nil) {
|
||||
[self dispatchCallback:^{
|
||||
completion(result);
|
||||
}];
|
||||
}
|
||||
_completion = nil;
|
||||
};
|
||||
|
||||
@synchronized(_outputStream) {
|
||||
if (_numberOfBytesLeftToWrite) {
|
||||
NSUInteger dataLength = (UInt32)_dataBeingWritten.length;
|
||||
if (_numberOfBytesLeftToWrite == dataLength) {
|
||||
GTMLoggerInfo(@"Starting a write operation of length %lu", (u_long)dataLength);
|
||||
}
|
||||
NSInteger numberOfPayloadBytesWritten =
|
||||
[_outputStream write:&_dataBeingWritten.bytes[dataLength - _numberOfBytesLeftToWrite]
|
||||
maxLength:_numberOfBytesLeftToWrite];
|
||||
|
||||
GTMLoggerInfo(@"Wrote %lu bytes", (u_long)numberOfPayloadBytesWritten);
|
||||
if (numberOfPayloadBytesWritten >= 0) {
|
||||
_numberOfBytesLeftToWrite -= numberOfPayloadBytesWritten;
|
||||
reportProgress(_dataBeingWritten.length - _numberOfBytesLeftToWrite);
|
||||
if (_numberOfBytesLeftToWrite < 0) {
|
||||
GTMLoggerInfo(@"Unexpected number of bytes written");
|
||||
_numberOfBytesLeftToWrite = 0;
|
||||
}
|
||||
if (_numberOfBytesLeftToWrite == 0) completed(GNCMPayloadResultSuccess);
|
||||
} else {
|
||||
GTMLoggerInfo(@"Error writing to output stream");
|
||||
completed(GNCMPayloadResultFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeStreams {
|
||||
@synchronized(_inputStream) {
|
||||
[_inputStream close];
|
||||
_inputStream = nil;
|
||||
}
|
||||
|
||||
@synchronized(_outputStream) {
|
||||
[_outputStream close];
|
||||
_outputStream = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GNCMBonjourService publishes mDNS type and domain with the specified service ID via Apple
|
||||
* Bonjour service. The mDNS type is a string formatted as "_[serviceIdHash]._tcp." in which
|
||||
* [serviceIdHash] is generated as a SHA-256 hash from the service ID and taken the 6 first bytes
|
||||
* of string in upper case.
|
||||
* When the service connects, the specified |endpointConnectedHandler| is called, which establishes
|
||||
* a connection to the browser.
|
||||
*
|
||||
* Don't hold the strong reference of caller self in endpointConnectedHandler to ensure there is no
|
||||
* retain cycle between them.
|
||||
*
|
||||
* @param serviceName A service name that embeds the |WifiLanServiceInfo| information.
|
||||
* @param serviceType An mDNS type that uniquely identifies the published service to search for.
|
||||
* @param port The requesting socket port number.
|
||||
* @param txtRecordData The TXTRecord data.
|
||||
* @param endpointConnectedHandler The handler that is called when a browser connects.
|
||||
*/
|
||||
@interface GNCMBonjourService : NSObject
|
||||
|
||||
- (instancetype)initWithServiceName:(NSString *)serviceName
|
||||
serviceType:(NSString *)serviceType
|
||||
port:(NSInteger)port
|
||||
TXTRecordData:(NSDictionary<NSString *, NSData *> *)TXTRecordData
|
||||
endpointConnectedHandler:(GNCMConnectionHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
@interface GNCMBonjourService () <NSNetServiceDelegate>
|
||||
|
||||
@property(nonatomic, copy) NSString *serviceName;
|
||||
@property(nonatomic, copy) NSString *serviceType;
|
||||
@property(nonatomic, copy) GNCMConnectionHandler endpointConnectedHandler;
|
||||
|
||||
@property(nonatomic) NSNetService *netService;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourService
|
||||
|
||||
- (instancetype)initWithServiceName:(NSString *)serviceName
|
||||
serviceType:(NSString *)serviceType
|
||||
port:(NSInteger)port
|
||||
TXTRecordData:(NSDictionary<NSString *, NSData *> *)TXTRecordData
|
||||
endpointConnectedHandler:(GNCMConnectionHandler)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_serviceName = [serviceName copy];
|
||||
_serviceType = [serviceType copy];
|
||||
_endpointConnectedHandler = handler;
|
||||
|
||||
_netService = [[NSNetService alloc] initWithDomain:GNCMBonjourDomain
|
||||
type:_serviceType
|
||||
name:_serviceName
|
||||
port:port];
|
||||
[_netService setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:TXTRecordData]];
|
||||
|
||||
_netService.delegate = self;
|
||||
[_netService scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_netService publishWithOptions:NSNetServiceListenForConnections];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_netService stop];
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceDelegate
|
||||
|
||||
- (void)netServiceDidPublish:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Did publish service: %@", service);
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didNotPublish:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Error publishing: service: %@, errorDic: %@", service, errorDict);
|
||||
}
|
||||
|
||||
- (void)netServiceDidStop:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Stopped publishing service: %@", service);
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didAcceptConnectionWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream {
|
||||
GTMLoggerDebug(@"Accepted connection, service: %@", service);
|
||||
GNCMBonjourConnection *connection =
|
||||
[[GNCMBonjourConnection alloc] initWithInputStream:inputStream
|
||||
outputStream:outputStream
|
||||
queue:nil];
|
||||
connection.connectionHandlers = _endpointConnectedHandler(connection);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// mDNS domain.
|
||||
FOUNDATION_EXPORT NSString *_Nonnull const GNCMBonjourDomain;
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
|
||||
NSString *const GNCMBonjourDomain = @"local";
|
||||
Reference in New Issue
Block a user