ios: nearbyConnections: Move source to //third_party.

PiperOrigin-RevId: 408046875
This commit is contained in:
edwinwu
2021-11-06 09:45:54 -07:00
committed by Copybara-Service
parent 8c2dd35eac
commit fb0337ebfa
76 changed files with 6192 additions and 7 deletions
@@ -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";