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,313 @@
|
||||
// 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/GNCAdvertiser.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/absl/functional/bind_front.h"
|
||||
#include "third_party/nearby_connections/cpp/core/core.h"
|
||||
#include "third_party/nearby_connections/cpp/core/listeners.h"
|
||||
#include "third_party/nearby_connections/cpp/core/options.h"
|
||||
#include "third_party/nearby_connections/cpp/core/params.h"
|
||||
#include "third_party/nearby_connections/cpp/core/status.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCPayloadListener.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCUtils.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
using ::location::nearby::ByteArrayFromNSData;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::ObjCStringFromCppString;
|
||||
using ::location::nearby::connections::ConnectionListener;
|
||||
using ::location::nearby::connections::ConnectionOptions;
|
||||
using ::location::nearby::connections::ConnectionRequestInfo;
|
||||
using ::location::nearby::connections::ConnectionResponseInfo;
|
||||
using ::location::nearby::connections::GNCStrategyToStrategy;
|
||||
using ::location::nearby::connections::Medium;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
using ::location::nearby::connections::Status;
|
||||
|
||||
/** This is a GNCAdvertiserConnectionInfo that provides storage for its properties. */
|
||||
@interface GNCAdvertiserConnectionInfo : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *name;
|
||||
@property(nonatomic, readonly) NSString *authToken;
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name authToken:(NSString *)authToken;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiserConnectionInfo
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name authToken:(NSString *)authToken {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_name = [name copy];
|
||||
_authToken = [authToken copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** Information retained about an endpoint before and after requesting a connection. */
|
||||
@interface GNCAdvertiserEndpointInfo : NSObject
|
||||
@property(nonatomic) GNCAdvertiserConnectionInfo *connectionInfo;
|
||||
@property(nonatomic) GNCConnectionResponse clientResponse;
|
||||
@property(nonatomic) BOOL clientResponseReceived; // whether the client response has been received
|
||||
@property(nonatomic, nullable) GNCConnectionResultHandlers *connectionResultHandlers;
|
||||
@property(nonatomic, weak) GNCCoreConnection *connection;
|
||||
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiserEndpointInfo
|
||||
|
||||
+ (instancetype)infoWithEndpointConnectionInfo:(GNCAdvertiserConnectionInfo *)connInfo {
|
||||
GNCAdvertiserEndpointInfo *info = [[GNCAdvertiserEndpointInfo alloc] init];
|
||||
info.connectionInfo = connInfo;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** GNCAdvertiser members. */
|
||||
@interface GNCAdvertiser ()
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic) GNCAdvertiserConnectionInitiationHandler initiationHandler;
|
||||
@property(nonatomic, assign) Status status;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, GNCAdvertiserEndpointInfo *> *endpoints;
|
||||
@end
|
||||
|
||||
/** C++ classes passed to the core library by GNCAdvertiser. */
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This class contains the callbacks for establishing and severing a connection. */
|
||||
class GNCAdvertiserConnectionListener {
|
||||
public:
|
||||
explicit GNCAdvertiserConnectionListener(GNCAdvertiser *advertiser) : advertiser_(advertiser) {}
|
||||
|
||||
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (endpointInfo) {
|
||||
GTMLoggerError(@"Connection already initiated for endpoint: %@", endpointId);
|
||||
} else {
|
||||
// TODO(b/169292092): endpointInfo is an advertisement byte array. Need to implement to
|
||||
// extract the endpoint name not just force to cast string.
|
||||
NSString *name = ObjCStringFromCppString(std::string(info.remote_endpoint_info));
|
||||
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
|
||||
GNCAdvertiserConnectionInfo *connInfo =
|
||||
[[GNCAdvertiserConnectionInfo alloc] initWithName:name authToken:authToken];
|
||||
endpointInfo = [GNCAdvertiserEndpointInfo infoWithEndpointConnectionInfo:connInfo];
|
||||
|
||||
// Call the connection initiation handler. Synchronous because it returns the connection
|
||||
// result handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
|
||||
endpointInfo.connectionResultHandlers = advertiser.initiationHandler(
|
||||
endpointId, (id<GNCAdvertiserConnectionInfo>)connInfo,
|
||||
^(GNCConnectionResponse response) {
|
||||
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
|
||||
endpointInfo.clientResponse = response;
|
||||
endpointInfo.clientResponseReceived = YES;
|
||||
if (response == GNCConnectionResponseAccept) {
|
||||
// The connection was accepted by the client.
|
||||
if (payload_listener_ == nullptr) {
|
||||
payload_listener_ = std::make_unique<GNCPayloadListener>(
|
||||
advertiser.core,
|
||||
^{
|
||||
return endpointInfo.connectionHandlers;
|
||||
},
|
||||
^{
|
||||
return endpointInfo.connection.payloads;
|
||||
});
|
||||
}
|
||||
advertiser.core->_core->AcceptConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
PayloadListener{
|
||||
.payload_cb = absl::bind_front(&GNCPayloadListener::OnPayload,
|
||||
payload_listener_.get()),
|
||||
.payload_progress_cb = absl::bind_front(
|
||||
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
|
||||
},
|
||||
ResultListener{});
|
||||
} else {
|
||||
// The connection was rejected by the client.
|
||||
advertiser.core->_core->RejectConnection(CppStringFromObjCString(endpointId),
|
||||
ResultListener{});
|
||||
}
|
||||
});
|
||||
});
|
||||
advertiser.endpoints[endpointId] = endpointInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAccepted(const std::string &endpoint_id) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (!endpointInfo) {
|
||||
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection has been accepted by both endpoints, so create the GNCConnection object
|
||||
// and pass it to |successHandler| for the client to use. It will be removed from |endpoints|
|
||||
// when the client disconnects (on dealloc of GNCConnection).
|
||||
// Note: Use a local strong reference to the connection object; don't just assign to
|
||||
// |endpointInfo.connection|. Without a strong reference, the connection object can be
|
||||
// deallocated before |successHandler| is called in the Release build.
|
||||
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
|
||||
id<GNCConnection> connection = [GNCCoreConnection
|
||||
connectionWithEndpointId:endpointId
|
||||
core:advertiser.core
|
||||
deallocHandler:^{
|
||||
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
|
||||
if (!strongAdvertiser) return;
|
||||
[strongAdvertiser.endpoints removeObjectForKey:endpointId];
|
||||
}];
|
||||
endpointInfo.connection = connection;
|
||||
|
||||
// Callback is synchronous because it returns the connection handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandlers =
|
||||
endpointInfo.connectionResultHandlers.successHandler(connection);
|
||||
});
|
||||
}
|
||||
|
||||
void OnRejected(const std::string &endpoint_id, Status status) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (!endpointInfo) {
|
||||
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
|
||||
return;
|
||||
}
|
||||
|
||||
// One side rejected, so call failureHandler with the connection status (we do this in all
|
||||
// cases), and forget the endpoint.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionResultHandlers.failureHandler(GNCConnectionFailureRejected);
|
||||
});
|
||||
[advertiser.endpoints removeObjectForKey:endpointId];
|
||||
}
|
||||
|
||||
void OnDisconnected(const std::string &endpoint_id) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (endpointInfo) {
|
||||
if (endpointInfo.connection) {
|
||||
GNCDisconnectedHandler disconnectedHandler =
|
||||
endpointInfo.connectionHandlers.disconnectedHandler;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
|
||||
});
|
||||
} else {
|
||||
GTMLoggerInfo(@"Disconnect for unconnected endpoint: %@", endpointId);
|
||||
}
|
||||
[advertiser.endpoints removeObjectForKey:endpointId];
|
||||
} else {
|
||||
GTMLoggerInfo(@"Disconnect for unknown endpoint: %@", endpointId);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
__weak GNCAdvertiser *advertiser_;
|
||||
std::unique_ptr<GNCPayloadListener> payload_listener_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
using ::location::nearby::connections::GNCAdvertiserConnectionListener;
|
||||
|
||||
@interface GNCAdvertiser () {
|
||||
std::unique_ptr<GNCAdvertiserConnectionListener> advertiserListener;
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiser
|
||||
|
||||
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
|
||||
serviceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
connectionInitiationHandler:
|
||||
(GNCAdvertiserConnectionInitiationHandler)initiationHandler {
|
||||
GNCAdvertiser *advertiser = [[GNCAdvertiser alloc] init];
|
||||
advertiser.initiationHandler = initiationHandler;
|
||||
advertiser.endpoints = [[NSMutableDictionary alloc] init];
|
||||
advertiser.core = GNCGetCore();
|
||||
advertiser->advertiserListener = std::make_unique<GNCAdvertiserConnectionListener>(advertiser);
|
||||
|
||||
ConnectionListener listener = {
|
||||
.initiated_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnInitiated,
|
||||
advertiser->advertiserListener.get()),
|
||||
.accepted_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnAccepted,
|
||||
advertiser->advertiserListener.get()),
|
||||
.rejected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnRejected,
|
||||
advertiser->advertiserListener.get()),
|
||||
.disconnected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnDisconnected,
|
||||
advertiser->advertiserListener.get()),
|
||||
};
|
||||
|
||||
advertiser.core->_core->StartAdvertising(CppStringFromObjCString(serviceId),
|
||||
ConnectionOptions{
|
||||
.strategy = GNCStrategyToStrategy(strategy),
|
||||
.auto_upgrade_bandwidth = true,
|
||||
.enforce_topology_constraints = true,
|
||||
},
|
||||
ConnectionRequestInfo{
|
||||
.endpoint_info = ByteArrayFromNSData(endpointInfo),
|
||||
.listener = std::move(listener),
|
||||
},
|
||||
ResultListener{});
|
||||
return advertiser;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLoggerInfo(@"GNCAdvertiser deallocated");
|
||||
_core->_core->StopAdvertising(ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/internal/service_controller_router.h"
|
||||
#include "platform/base/payload_id.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This class contains the C++ Core object. */
|
||||
@interface GNCCore : NSObject {
|
||||
@public
|
||||
std::unique_ptr<::location::nearby::connections::Core> _core;
|
||||
std::unique_ptr<::location::nearby::connections::ServiceControllerRouter>
|
||||
_service_controller_router;
|
||||
}
|
||||
|
||||
/**
|
||||
* These functions are the utilities to manipulate the InputFile in ImplementationPlatform for
|
||||
* sending File payload.
|
||||
*
|
||||
* Inserts the URL to the map, keyed by payloadID. The element will not be inserted if there
|
||||
* already is an element with the key in the map.
|
||||
*/
|
||||
- (void)insertURLToMapWithPayloadID:(::location::nearby::PayloadId)payloadId urlToSend:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* Returns the URL with the payloadID and removes the entry from the map. Returns nil if
|
||||
* payloadID is not found.
|
||||
*/
|
||||
- (nullable NSURL *)extractURLWithPayloadID:(::location::nearby::PayloadId)payloadId;
|
||||
|
||||
- (void)clearSendingURLMaps;
|
||||
|
||||
@end
|
||||
|
||||
/** This function returns the Core singleton, wrapped in an Obj-C object for lifetime management. */
|
||||
GNCCore *GNCGetCore();
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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/Internal/GNCCore.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "third_party/absl/container/flat_hash_map.h"
|
||||
#include "third_party/absl/container/internal/common.h"
|
||||
#include "third_party/nearby_connections/cpp/core/core.h"
|
||||
#include "third_party/nearby_connections/cpp/core/internal/service_controller_router.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/payload_id.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
using ::location::nearby::connections::Core;
|
||||
using ::location::nearby::PayloadId;
|
||||
using ::location::nearby::connections::ServiceControllerRouter;
|
||||
|
||||
@implementation GNCCore {
|
||||
// A map to store the NSURL object with PayloadId for sendFilePayload in GNCConnection.
|
||||
// This is the place to store the NSURL for InputFile creation in ImplementationPlatform.
|
||||
absl::flat_hash_map<PayloadId, NSURL *> _sending_urls;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
GTMLoggerInfo(@"GNCCore created");
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_service_controller_router = std::make_unique<ServiceControllerRouter>();
|
||||
_core = std::make_unique<Core>(_service_controller_router.get());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_core.reset();
|
||||
_service_controller_router.reset();
|
||||
GTMLoggerInfo(@"GNCCore deallocated");
|
||||
}
|
||||
|
||||
- (void)insertURLToMapWithPayloadID:(PayloadId)payloadId urlToSend:(NSURL *)url {
|
||||
_sending_urls.emplace(payloadId, url);
|
||||
}
|
||||
|
||||
- (nullable NSURL *)extractURLWithPayloadID:(PayloadId)payloadId {
|
||||
NSURL *url;
|
||||
auto it = _sending_urls.find(payloadId);
|
||||
if (it != _sending_urls.end()) {
|
||||
auto pair = _sending_urls.extract(it);
|
||||
url = pair.mapped();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
- (void)clearSendingURLMaps {
|
||||
_sending_urls.clear();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
GNCCore *GNCGetCore() {
|
||||
static NSObject *syncSingleton;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
syncSingleton = [[NSObject alloc] init];
|
||||
});
|
||||
|
||||
// The purpose of keeping a weak reference to the GNCCore object is to ensure that it will be
|
||||
// released when all external strong references are gone. I.e., when the app is no longer doing
|
||||
// any NC operations, the core will be released.
|
||||
static __weak GNCCore *core;
|
||||
|
||||
// Strongly reference the GNCCore object for the duration of this function to ensure it isn't
|
||||
// prematurely deallocated by ARC after being created (which can happen in optimized builds).
|
||||
GNCCore *strongCore = core;
|
||||
@synchronized(syncSingleton) {
|
||||
if (!strongCore) {
|
||||
strongCore = [[GNCCore alloc] init];
|
||||
core = strongCore;
|
||||
}
|
||||
}
|
||||
return strongCore;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This holds the progress and completion for a pending payload. */
|
||||
@interface GNCPayloadInfo : NSObject
|
||||
@property(nonatomic, nullable) NSProgress *progress;
|
||||
@property(nonatomic, nullable) GNCPayloadResultHandler completion;
|
||||
|
||||
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
- (void)callCompletion:(GNCPayloadResult)result;
|
||||
@end
|
||||
|
||||
/** GNCConnection that interfaces with the Core library. */
|
||||
@interface GNCCoreConnection : NSObject <GNCConnection>
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic, copy) GNCEndpointId endpointId;
|
||||
@property(nonatomic) dispatch_block_t deallocHandler;
|
||||
@property(nonatomic) NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads;
|
||||
|
||||
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
|
||||
core:(GNCCore *)core
|
||||
deallocHandler:(dispatch_block_t)deallocHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,188 @@
|
||||
// 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/Internal/GNCCoreConnection.h"
|
||||
|
||||
#include "third_party/nearby_connections/cpp/core/core.h"
|
||||
#include "third_party/nearby_connections/cpp/core/payload.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/input_file.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/base/exception.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/input_stream.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/base/payload_id.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCPayload.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/public/file.h"
|
||||
|
||||
using ::location::nearby::ByteArrayFromNSData;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::InputFile;
|
||||
using ::location::nearby::InputStream;
|
||||
using ::location::nearby::connections::Payload;
|
||||
using ::location::nearby::PayloadId;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/**
|
||||
* This InputStream subclass takes input from an NSInputStream. The update handler is called for
|
||||
* each chunk of data sent, giving the client an opportunity to handle cancelation.
|
||||
*/
|
||||
class GNCInputStreamFromNSStream : public InputStream {
|
||||
public:
|
||||
explicit GNCInputStreamFromNSStream(NSInputStream *nsStream) : nsStream_(nsStream) {
|
||||
[nsStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[nsStream open];
|
||||
}
|
||||
|
||||
~GNCInputStreamFromNSStream() override { Close(); }
|
||||
|
||||
ExceptionOr<ByteArray> Read() { return Read(kMaxChunkSize); }
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override {
|
||||
uint8_t *bytesRead = new uint8_t[size];
|
||||
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
|
||||
NSInteger numberOfBytesRead = [nsStream_ read:bytesRead maxLength:numberOfBytesToRead];
|
||||
if (numberOfBytesRead == 0) {
|
||||
// Reached end of stream.
|
||||
return ExceptionOr<ByteArray>();
|
||||
} else if (numberOfBytesRead < 0) {
|
||||
// Stream error.
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData([NSData dataWithBytes:bytesRead
|
||||
length:numberOfBytesRead]));
|
||||
}
|
||||
|
||||
Exception Close() override {
|
||||
[nsStream_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t kMaxChunkSize = 32 * 1024;
|
||||
NSInputStream *nsStream_;
|
||||
// dispatch_block_t update_handler_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@implementation GNCPayloadInfo
|
||||
|
||||
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
GNCPayloadInfo *info = [[GNCPayloadInfo alloc] init];
|
||||
info.progress = progress;
|
||||
info.completion = completion;
|
||||
return info;
|
||||
}
|
||||
|
||||
- (void)callCompletion:(GNCPayloadResult)result {
|
||||
if (_completion) _completion(result);
|
||||
_completion = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCCoreConnection
|
||||
|
||||
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
|
||||
core:(GNCCore *)core
|
||||
deallocHandler:(dispatch_block_t)deallocHandler {
|
||||
GNCCoreConnection *connection = [[GNCCoreConnection alloc] init];
|
||||
connection.endpointId = endpointId;
|
||||
connection.core = core;
|
||||
connection.deallocHandler = deallocHandler;
|
||||
connection.payloads = [[NSMutableDictionary alloc] init];
|
||||
return connection;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_core->_core->DisconnectFromEndpoint(CppStringFromObjCString(_endpointId), ResultListener{});
|
||||
_deallocHandler();
|
||||
}
|
||||
|
||||
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
Payload corePayload(ByteArrayFromNSData(payload.bytes));
|
||||
NSUInteger length = payload.bytes.length;
|
||||
PayloadId payloadId = corePayload.GetId();
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:length];
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
progress.cancellationHandler = ^{
|
||||
[weakSelf cancelPayloadWithId:payloadId];
|
||||
};
|
||||
return [self sendPayload:std::move(corePayload)
|
||||
size:length
|
||||
progress:progress
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1];
|
||||
|
||||
PayloadId payloadId = Payload::GenerateId();
|
||||
Payload corePayload(payloadId, [payload]() -> InputStream & {
|
||||
location::nearby::connections::GNCInputStreamFromNSStream *stream =
|
||||
new location::nearby::connections::GNCInputStreamFromNSStream(payload.stream);
|
||||
return *stream;
|
||||
});
|
||||
return [self sendPayload:std::move(corePayload) size:-1 progress:progress completion:completion];
|
||||
}
|
||||
|
||||
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:0];
|
||||
|
||||
std::int64_t fileSize = 0;
|
||||
NSURL *fileURL = payload.fileURL;
|
||||
NSNumber *fileSizeValue = nil;
|
||||
BOOL result = [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
|
||||
if (result == YES) {
|
||||
fileSize = fileSizeValue.longValue;
|
||||
}
|
||||
PayloadId payloadId = Payload::GenerateId();
|
||||
// Add the pair of payloadId and fileURL to the map in the GNCCore.
|
||||
[_core insertURLToMapWithPayloadID:payloadId urlToSend:fileURL];
|
||||
Payload corePayload(payloadId, InputFile(payloadId, fileSize));
|
||||
progress.totalUnitCount = fileSize;
|
||||
return [self sendPayload:std::move(corePayload)
|
||||
size:fileSize
|
||||
progress:progress
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (NSProgress *)sendPayload:(Payload)payload
|
||||
size:(uint64_t)size
|
||||
progress:(NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
_payloads[@(payload.GetId())] = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
_core->_core->SendPayload(std::vector<std::string>(1, CppStringFromObjCString(_endpointId)),
|
||||
std::move(payload), ResultListener{});
|
||||
return progress;
|
||||
}
|
||||
|
||||
- (void)cancelPayloadWithId:(PayloadId)payloadId {
|
||||
_core->_core->CancelPayload(payloadId, ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,394 @@
|
||||
// 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/GNCDiscoverer.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "third_party/absl/functional/bind_front.h"
|
||||
#include "third_party/nearby_connections/cpp/core/core.h"
|
||||
#include "third_party/nearby_connections/cpp/core/listeners.h"
|
||||
#include "third_party/nearby_connections/cpp/core/options.h"
|
||||
#include "third_party/nearby_connections/cpp/core/status.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCPayloadListener.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCUtils.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
using ::location::nearby::ByteArray;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::connections::ConnectionOptions;
|
||||
using ::location::nearby::connections::DiscoveryListener;
|
||||
using ::location::nearby::connections::DistanceInfo;
|
||||
using ::location::nearby::connections::GNCStrategyToStrategy;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
using ::location::nearby::connections::Status;
|
||||
|
||||
/** This is a GNCDiscovererConnectionInfo that provides storage for its properties. */
|
||||
@interface GNCDiscovererConnectionInfo : NSObject <GNCDiscovererConnectionInfo>
|
||||
@property(nonatomic, copy) NSString *authToken;
|
||||
|
||||
/** Creates a GNCDiscovererConnectionInfo object. */
|
||||
+ (instancetype)infoWithAuthToken:(NSString *)authToken;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscovererConnectionInfo
|
||||
|
||||
+ (instancetype)infoWithAuthToken:(NSString *)authToken {
|
||||
GNCDiscovererConnectionInfo *info = [[GNCDiscovererConnectionInfo alloc] init];
|
||||
info.authToken = authToken;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** This is a GNCDiscoveredEndpointInfo that provides storage for its properties. */
|
||||
@interface GNCDiscoveredEndpointInfo : NSObject <GNCDiscoveredEndpointInfo>
|
||||
@property(nonatomic, copy) NSString *name;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscoveredEndpointInfo
|
||||
|
||||
@synthesize requestConnection = _requestConnection;
|
||||
|
||||
+ (instancetype)infoWithName:(NSString *)name
|
||||
requestConnection:(GNCConnectionRequester)requestConnection {
|
||||
GNCDiscoveredEndpointInfo *info = [[GNCDiscoveredEndpointInfo alloc] init];
|
||||
info.name = name;
|
||||
info->_requestConnection = requestConnection;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** Information retained by the discoverer about each discovered endpoint. */
|
||||
@interface GNCDiscovererEndpointInfo : NSObject
|
||||
|
||||
/** Handles lostHandler once |onEndpointLost| has been callback. */
|
||||
@property(nonatomic) GNCEndpointLostHandler lostHandler;
|
||||
|
||||
/** The connInitHandler is stored after requestConnection. */
|
||||
@property(nonatomic, nullable) GNCDiscovererConnectionInitializationHandler connInitHandler;
|
||||
|
||||
/** The connFailureHandler is stored after requestConnection. */
|
||||
@property(nonatomic, nullable) GNCConnectionFailureHandler connFailureHandler;
|
||||
|
||||
/** Client responses Accept or Reject. */
|
||||
@property(nonatomic) GNCConnectionResponse clientResponse;
|
||||
|
||||
/** Whether the client response has been received. */
|
||||
@property(nonatomic) BOOL clientResponseReceived;
|
||||
|
||||
/**
|
||||
* The connectionhandler returned by connInitHandler. Stored here if the connection is accepted.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCConnectionHandler connectionHandler;
|
||||
|
||||
/** @c GNCCoreConnection is created and stored if connection is accepted. */
|
||||
@property(nonatomic, weak) GNCCoreConnection *connection;
|
||||
|
||||
/** @c GNCConnectionHandlers object is returned by connectionHandler and stored here. */
|
||||
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscovererEndpointInfo
|
||||
@end
|
||||
|
||||
/** GNCDiscoverer members. */
|
||||
@interface GNCDiscoverer ()
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic) GNCEndpointFoundHandler endpointFoundHandler;
|
||||
@property(nonatomic, assign) Status status;
|
||||
@property(nonatomic) NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints;
|
||||
@end
|
||||
|
||||
/** C++ classes passed to the core library by GNCDiscoverer. */
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This class contains the discoverer callbacks related to a connection. */
|
||||
class GNCDiscovererConnectionListener {
|
||||
public:
|
||||
GNCDiscovererConnectionListener(GNCCore *core,
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints)
|
||||
: core_(core), endpoints_(endpoints) {}
|
||||
|
||||
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the connection initiation handler. Synchronous because it returns the connection
|
||||
// handler.
|
||||
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
|
||||
GNCCore *core = core_; // don't capture |this|
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandler = endpointInfo.connInitHandler(
|
||||
[GNCDiscovererConnectionInfo infoWithAuthToken:authToken],
|
||||
^(GNCConnectionResponse response) {
|
||||
endpointInfo.clientResponse = response;
|
||||
endpointInfo.clientResponseReceived = YES;
|
||||
if (response == GNCConnectionResponseAccept) {
|
||||
// The connect was accepted by the client.
|
||||
if (payload_listener_ == nullptr) {
|
||||
payload_listener_ = std::make_unique<GNCPayloadListener>(
|
||||
core,
|
||||
^{
|
||||
return endpointInfo.connectionHandlers;
|
||||
},
|
||||
^{
|
||||
return endpointInfo.connection.payloads;
|
||||
});
|
||||
}
|
||||
core->_core->AcceptConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
PayloadListener{
|
||||
.payload_cb =
|
||||
absl::bind_front(&GNCPayloadListener::OnPayload, payload_listener_.get()),
|
||||
.payload_progress_cb = absl::bind_front(
|
||||
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
|
||||
},
|
||||
ResultListener{});
|
||||
} else {
|
||||
// The connect was rejected by the client.
|
||||
core->_core->RejectConnection(CppStringFromObjCString(endpointId), ResultListener{});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void OnAccepted(const std::string &endpoint_id) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection has been accepted by both endpoints, so create the GNCConnection object
|
||||
// and pass it to |successHandler| for the client to use.
|
||||
// Note: Use a local strong reference to the connection object; don't just assign to
|
||||
// |endpointInfo.connection|. Without a strong reference, the connection object can be
|
||||
// deallocated before |successHandler| is called in the Release build.
|
||||
id<GNCConnection> connection = [GNCCoreConnection
|
||||
connectionWithEndpointId:endpointId
|
||||
core:core_
|
||||
deallocHandler:^{
|
||||
// Don't remove the remote endpoint (like GNCAdvertiser does) because that's
|
||||
// done when the endpoint is lost.
|
||||
}];
|
||||
endpointInfo.connection = connection;
|
||||
|
||||
// Callback is synchronous because it returns the connection handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandlers = endpointInfo.connectionHandler(endpointInfo.connection);
|
||||
});
|
||||
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
|
||||
}
|
||||
|
||||
void OnRejected(const std::string &endpoint_id, Status status) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If either side rejected, call failureHandler with the connection status.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connFailureHandler(GNCConnectionFailureRejected);
|
||||
});
|
||||
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
|
||||
}
|
||||
|
||||
void OnDisconnected(const std::string &endpoint_id) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpointInfo.connection) {
|
||||
GNCDisconnectedHandler disconnectedHandler =
|
||||
endpointInfo.connectionHandlers.disconnectedHandler;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
GNCCore *core_;
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints_;
|
||||
std::unique_ptr<GNCPayloadListener> payload_listener_;
|
||||
};
|
||||
|
||||
class GNCDiscoveryListener {
|
||||
public:
|
||||
explicit GNCDiscoveryListener(GNCDiscoverer *discoverer) : discoverer_(discoverer) {}
|
||||
|
||||
void OnEndpointFound(const std::string &endpoint_id, const ByteArray &endpoint_info,
|
||||
const std::string &service_id) {
|
||||
GNCDiscoverer *discoverer = discoverer_; // strongify
|
||||
if (!discoverer) {
|
||||
return;
|
||||
}
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
|
||||
if ([discoverer.endpoints objectForKey:endpointId] != nil) {
|
||||
GTMLoggerError(@"Endpoint already discovered: %@", endpointId);
|
||||
} else {
|
||||
// The GNCDiscoveredEndpointInfo object created here lives as long as the client has strong
|
||||
// reference to it. Here's the chain of strong references maintained here:
|
||||
// client -> GNCDiscoveredEndpointInfo -> RequestConnection block ->
|
||||
// GNCDiscovererEndpointInfo (stored weakly in the |endpoints| map table)
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [[GNCDiscovererEndpointInfo alloc] init];
|
||||
[discoverer.endpoints setObject:endpointInfo forKey:endpointId];
|
||||
|
||||
// TODO(b/169292092): endpointInfo is an advertisement byte array. Need to implement to
|
||||
// extract the endpoint name not just force to cast string.
|
||||
NSString *name = ObjCStringFromCppString(std::string(endpoint_info));
|
||||
GNCCore *core = discoverer.core; // don't capture |this| or |discoverer|
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints = discoverer.endpoints;
|
||||
GNCDiscoveredEndpointInfo *discEndpointInfo = [GNCDiscoveredEndpointInfo
|
||||
infoWithName:name
|
||||
requestConnection:^(NSString *name,
|
||||
GNCDiscovererConnectionInitializationHandler connInitHandler,
|
||||
GNCConnectionFailureHandler connFailureHandler) {
|
||||
endpointInfo.connInitHandler = connInitHandler;
|
||||
endpointInfo.connFailureHandler = connFailureHandler;
|
||||
if (discoverer_connection_listener_ == nullptr) {
|
||||
discoverer_connection_listener_ =
|
||||
std::make_unique<GNCDiscovererConnectionListener>(core, endpoints);
|
||||
}
|
||||
|
||||
ConnectionListener listener = {
|
||||
.initiated_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnInitiated,
|
||||
discoverer_connection_listener_.get()),
|
||||
.accepted_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnAccepted,
|
||||
discoverer_connection_listener_.get()),
|
||||
.rejected_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnRejected,
|
||||
discoverer_connection_listener_.get()),
|
||||
.disconnected_cb =
|
||||
absl::bind_front(&GNCDiscovererConnectionListener::OnDisconnected,
|
||||
discoverer_connection_listener_.get()),
|
||||
};
|
||||
|
||||
core->_core->RequestConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
ConnectionRequestInfo{.endpoint_info = std::move(endpoint_info),
|
||||
.listener = std::move(listener)},
|
||||
ConnectionOptions{},
|
||||
ResultListener{.result_cb = [&connFailureHandler](Status status) {
|
||||
if (!status.Ok()) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
connFailureHandler(GNCConnectionFailureUnknown);
|
||||
});
|
||||
}
|
||||
}});
|
||||
}];
|
||||
|
||||
// Call the client endpoint-found handler. Tail call for reentrancy.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.lostHandler = discoverer.endpointFoundHandler(endpointId, discEndpointInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnEndpointLost(const std::string &endpoint_id) {
|
||||
GNCDiscoverer *discoverer = discoverer_; // strongify
|
||||
if (!discoverer) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *info = [discoverer.endpoints objectForKey:endpointId];
|
||||
if (!info) {
|
||||
GTMLoggerError(@"Endpoint already lost: %@", endpointId);
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
info.lostHandler();
|
||||
});
|
||||
}
|
||||
[discoverer.endpoints removeObjectForKey:endpointId];
|
||||
}
|
||||
|
||||
void OnEndpointDistanceChanged_cb(const std::string &endpoint_id, DistanceInfo info) {
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
__weak GNCDiscoverer *discoverer_;
|
||||
std::unique_ptr<GNCDiscovererConnectionListener> discoverer_connection_listener_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
using ::location::nearby::connections::GNCDiscoveryListener;
|
||||
|
||||
@interface GNCDiscoverer () {
|
||||
std::unique_ptr<GNCDiscoveryListener> discoveryListener;
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCDiscoverer
|
||||
|
||||
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler {
|
||||
GNCDiscoverer *discoverer = [[GNCDiscoverer alloc] init];
|
||||
discoverer.endpointFoundHandler = endpointFoundHandler;
|
||||
discoverer.endpoints = [NSMapTable strongToWeakObjectsMapTable];
|
||||
discoverer.core = GNCGetCore();
|
||||
discoverer->discoveryListener = std::make_unique<GNCDiscoveryListener>(discoverer);
|
||||
|
||||
DiscoveryListener listener = {
|
||||
.endpoint_found_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointFound,
|
||||
discoverer->discoveryListener.get()),
|
||||
.endpoint_lost_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointLost,
|
||||
discoverer->discoveryListener.get()),
|
||||
};
|
||||
|
||||
discoverer.core->_core->StartDiscovery(CppStringFromObjCString(serviceId),
|
||||
ConnectionOptions{
|
||||
.strategy = GNCStrategyToStrategy(strategy),
|
||||
},
|
||||
std::move(listener), ResultListener{});
|
||||
|
||||
return discoverer;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLoggerInfo(@"GNCDiscoverer deallocated");
|
||||
_core->_core->StopDiscovery(ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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/GNCPayload.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCBytesPayload (Internal)
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier;
|
||||
@end
|
||||
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCStreamPayload (Internal)
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier;
|
||||
@end
|
||||
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCFilePayload (Internal)
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
|
||||
@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/GNCPayload.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
uint64_t GNCRandom64() {
|
||||
return ((uint64_t)arc4random() << 32) + arc4random();
|
||||
}
|
||||
|
||||
@implementation GNCBytesPayload
|
||||
|
||||
- (instancetype)initWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_bytes = bytes;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes {
|
||||
return [[self alloc] initWithBytes:bytes identifier:GNCRandom64()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithBytes:bytes identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCStreamPayload
|
||||
|
||||
- (instancetype)initWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_stream = stream;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream {
|
||||
return [[self alloc] initWithStream:stream identifier:GNCRandom64()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithStream:stream identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCFilePayload
|
||||
|
||||
- (instancetype)initWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_fileURL = [fileURL copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL {
|
||||
return [[self alloc] initWithFileURL:fileURL identifier:GNCRandom64()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithFileURL:fileURL identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GNCPayloadInfo;
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This fetches a GNCConnectionHandlers object. */
|
||||
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandlersProvider)();
|
||||
|
||||
/** This fetches a payload dictionary. */
|
||||
typedef NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *_Nonnull (^GNCPayloadsProvider)();
|
||||
|
||||
/** This is the payload handler for an advertiser or discoverer. */
|
||||
class GNCPayloadListener : public PayloadListener {
|
||||
public:
|
||||
GNCPayloadListener(GNCCore *core, GNCConnectionHandlersProvider handlersProvider,
|
||||
GNCPayloadsProvider payloadsProvider)
|
||||
: core_(core), handlers_provider_(handlersProvider), payloads_provider_(payloadsProvider) {}
|
||||
|
||||
void OnPayload(const std::string& endpoint_id, Payload payload);
|
||||
void OnPayloadProgress(const std::string& endpoint_id,
|
||||
const PayloadProgressInfo& info);
|
||||
|
||||
private:
|
||||
GNCCore *core_;
|
||||
GNCConnectionHandlersProvider handlers_provider_;
|
||||
GNCPayloadsProvider payloads_provider_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,218 @@
|
||||
// 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/Internal/GNCPayloadListener.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/nearby_connections/cpp/core/core.h"
|
||||
#include "third_party/nearby_connections/cpp/core/listeners.h"
|
||||
#include "third_party/nearby_connections/cpp/core/payload.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/exception.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/input_stream.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCPayload.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCPayload+Internal.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/public/file.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
void GNCPayloadListener::OnPayload(const std::string &endpoint_id, Payload payload) {
|
||||
GNCConnectionHandlers *handlers = handlers_provider_();
|
||||
int64_t payloadId = payload.GetId();
|
||||
|
||||
// Note: The payload must be destroyed by each individual payload type handler below, because in
|
||||
// the Stream payload case, it runs an asynchronous read-write loop, which needs the payload
|
||||
// and its stream to live until the stream ends.
|
||||
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
|
||||
|
||||
switch (payload.GetType()) {
|
||||
case Payload::Type::kBytes: {
|
||||
NSData *data = NSDataFromByteArray(payload.AsBytes()); // don't capture C++ object
|
||||
|
||||
// Wait for the payload transfer update to arrive before calling the Bytes payload handler.
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo
|
||||
infoWithProgress:nil
|
||||
completion:^(GNCPayloadResult result) {
|
||||
NSCAssert(result == GNCPayloadResultSuccess, @"Expected success");
|
||||
if (handlers.bytesPayloadHandler) {
|
||||
// Call the Bytes payload handler.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handlers.bytesPayloadHandler([GNCBytesPayload payloadWithBytes:data
|
||||
identifier:payloadId]);
|
||||
});
|
||||
}
|
||||
}];
|
||||
payloads[@(payloadId)] = info;
|
||||
break;
|
||||
}
|
||||
|
||||
case Payload::Type::kStream:
|
||||
if (handlers.streamPayloadHandler) {
|
||||
// Make a pair of bound streams so data pumped into the output stream becomes
|
||||
// available for reading from the input stream.
|
||||
NSInputStream *clientInputStream;
|
||||
NSOutputStream *clientOutputStream;
|
||||
// TODO(b/169292092): Base on medium's bandwidth?
|
||||
[NSStream getBoundStreamsWithBufferSize:1024
|
||||
inputStream:&clientInputStream
|
||||
outputStream:&clientOutputStream];
|
||||
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1]; // indeterminate
|
||||
progress.cancellable = YES;
|
||||
|
||||
// Pass the payload to the stream payload handler, receiving the completion handler from it.
|
||||
// Since it returns a value, it must be called synchronously.
|
||||
__block GNCPayloadResultHandler completion;
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
completion = handlers.streamPayloadHandler(
|
||||
[GNCStreamPayload payloadWithStream:clientInputStream identifier:payloadId],
|
||||
progress);
|
||||
});
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
payloads[@(payloadId)] = info;
|
||||
|
||||
// This is a loop that reads data from the C++ input stream and writes it to the output
|
||||
// stream that feeds it to the client input stream.
|
||||
__block InputStream *payloadInputStream = payload.AsStream();
|
||||
dispatch_queue_t queue =
|
||||
dispatch_queue_create("StreamReceiverQueue", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
[clientOutputStream open];
|
||||
while (true) {
|
||||
if (progress.isCancelled) {
|
||||
// Payload was canceled by the client.
|
||||
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
|
||||
// TODO(b/148640962): Implement.
|
||||
}});
|
||||
break;
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> readResult = payloadInputStream->Read(1024);
|
||||
if (!readResult.ok()) {
|
||||
// Error reading from stream.
|
||||
// TODO(b/169292092): Tell core an error has occurred?
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[info callCompletion:GNCPayloadResultFailure];
|
||||
});
|
||||
break;
|
||||
}
|
||||
ByteArray byteArray = readResult.GetResult();
|
||||
if (byteArray.Empty()) {
|
||||
// End of stream.
|
||||
break;
|
||||
}
|
||||
|
||||
// Loop until it's all been consumed by the client output stream.
|
||||
NSData *data = NSDataFromByteArray(byteArray);
|
||||
NSUInteger totalLength = data.length;
|
||||
NSUInteger totalNumberWritten = 0;
|
||||
while (totalNumberWritten < totalLength) {
|
||||
NSInteger numberWritten =
|
||||
[clientOutputStream write:&((const uint8_t *)data.bytes)[totalNumberWritten]
|
||||
maxLength:totalLength - totalNumberWritten];
|
||||
if (numberWritten <= 0) { // stream error or reached end of stream
|
||||
// TODO(b/169292092): Tell core an error has occurred?
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[info callCompletion:GNCPayloadResultFailure];
|
||||
});
|
||||
break;
|
||||
}
|
||||
totalNumberWritten += numberWritten;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case Payload::Type::kFile:
|
||||
if (handlers.filePayloadHandler) {
|
||||
InputFile *payloadInputFile = payload.AsFile();
|
||||
NSURL *fileURL =
|
||||
[NSURL URLWithString:ObjCStringFromCppString(payloadInputFile->GetFilePath())];
|
||||
int64_t fileSize = payloadInputFile->GetTotalSize();
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:fileSize];
|
||||
progress.cancellable = YES;
|
||||
progress.cancellationHandler = ^{
|
||||
// Payload was canceled by the client.
|
||||
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
|
||||
// TODO(b/148640962): Implement.
|
||||
}});
|
||||
};
|
||||
|
||||
// Pass the payload to the file payload handler, receiving the completion handler from it.
|
||||
// Since it returns a value, it must be called synchronously.
|
||||
__block GNCPayloadResultHandler completion;
|
||||
void (^passPayloadBlock)(void) = ^{
|
||||
completion = handlers.filePayloadHandler(
|
||||
[GNCFilePayload payloadWithFileURL:fileURL identifier:payloadId], progress);
|
||||
};
|
||||
if ([NSThread isMainThread]) {
|
||||
passPayloadBlock();
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), passPayloadBlock);
|
||||
}
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
payloads[@(payloadId)] = info;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
;// fall through
|
||||
}
|
||||
}
|
||||
|
||||
void GNCPayloadListener::OnPayloadProgress(const std::string &endpoint_id,
|
||||
const PayloadProgressInfo &info) {
|
||||
// Note: The logic in this callback for handling progress updates and payload completion is
|
||||
// identical for Bytes, Stream and File payloads.
|
||||
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
|
||||
NSNumber *payloadId = @(info.payload_id);
|
||||
GNCPayloadInfo *payloadInfo = payloads[payloadId];
|
||||
if (payloadInfo) {
|
||||
// Update the progress.
|
||||
if (payloadInfo.progress) {
|
||||
payloadInfo.progress.completedUnitCount = info.bytes_transferred;
|
||||
}
|
||||
|
||||
// Call the completion handler for success/failure/canceled, but not in-progress.
|
||||
if (info.status == PayloadProgressInfo::Status::kInProgress) {
|
||||
return;
|
||||
}
|
||||
GNCPayloadResult result =
|
||||
(info.status == PayloadProgressInfo::Status::kSuccess) ? GNCPayloadResultSuccess
|
||||
: (info.status == PayloadProgressInfo::Status::kCanceled) ? GNCPayloadResultCanceled
|
||||
: GNCPayloadResultFailure;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
payloadInfo.completion(result);
|
||||
});
|
||||
|
||||
// Release the payload info.
|
||||
[payloads removeObjectForKey:payloadId];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/listeners.h"
|
||||
#include "core/options.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCAdvertiser.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** Converts GNCStrategy to Strategy. */
|
||||
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy);
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
/** Internal-only properties of the connection result handlers class. */
|
||||
@interface GNCConnectionResultHandlers ()
|
||||
@property(nonatomic) GNCConnectionHandler successHandler;
|
||||
@property(nonatomic) GNCConnectionFailureHandler failureHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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/Internal/GNCUtils.h"
|
||||
|
||||
#include "third_party/nearby_connections/cpp/core/strategy.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCAdvertiser.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy) {
|
||||
switch (strategy) {
|
||||
case GNCStrategyCluster:
|
||||
return Strategy::kP2pCluster;
|
||||
case GNCStrategyStar:
|
||||
return Strategy::kP2pStar;
|
||||
case GNCStrategyPointToPoint:
|
||||
return Strategy::kP2pPointToPoint;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@implementation GNCConnectionHandlers
|
||||
|
||||
- (instancetype)initWithBuilderBlock:(void (^)(GNCConnectionHandlers*))builderBlock {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
builderBlock(self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers * _Nonnull))builderBlock {
|
||||
return [[self alloc] initWithBuilderBlock:builderBlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCConnectionResultHandlers
|
||||
|
||||
- (instancetype)initWithSuccessHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_successHandler = successHandler;
|
||||
_failureHandler = failureHandler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler {
|
||||
return [[self alloc] initWithSuccessHandler:successHandler failureHandler:failureHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,151 @@
|
||||
// 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.
|
||||
|
||||
#include "third_party/nearby_connections/cpp/platform/api/platform.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/nearby_connections/cpp/platform/api/mutex.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/payload_id.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Internal/GNCCore.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/atomic_boolean.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/atomic_uint32.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/condition_variable.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/count_down_latch.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/input_file.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/log_message.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/multi_thread_executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/mutex.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/single_thread_executor.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/wifi_lan.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/shared/file.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
std::string GetPayloadPath(PayloadId payload_id) {
|
||||
// This is to get a file path, e.g. /tmp/[payload_id], for the storage of payload file.
|
||||
// NOTE: Per
|
||||
// https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
|
||||
// Files saved in the /tmp directory will be deleted by the system. Callers should be responsible
|
||||
// for copying the files to the permanent storage.
|
||||
NSString *payloadIdString = ObjCStringFromCppString(std::to_string(payload_id));
|
||||
return CppStringFromObjCString(
|
||||
[NSTemporaryDirectory() stringByAppendingPathComponent:payloadIdString]);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Atomics:
|
||||
std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(bool initial_value) {
|
||||
return std::make_unique<ios::AtomicBoolean>(initial_value);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
|
||||
std::uint32_t initial_value) {
|
||||
return std::make_unique<ios::AtomicUint32>(initial_value);
|
||||
}
|
||||
|
||||
std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(std::int32_t count) {
|
||||
return std::make_unique<ios::CountDownLatch>(count);
|
||||
}
|
||||
|
||||
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
|
||||
// iOS does not support unchecked Mutex in debug mode, therefore
|
||||
// ios::Mutex is used for both kRegular and kRegularNoCheck.
|
||||
if (mode == Mutex::Mode::kRecursive) {
|
||||
return absl::make_unique<ios::RecursiveMutex>();
|
||||
} else {
|
||||
return absl::make_unique<ios::Mutex>();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ConditionVariable> ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
|
||||
return std::make_unique<ios::ConditionVariable>(static_cast<ios::Mutex*>(mutex));
|
||||
}
|
||||
|
||||
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(PayloadId payload_id,
|
||||
std::int64_t total_size) {
|
||||
// Extract the NSURL object with payload_id from |GNCCore| which stores the maps. If the retrieved
|
||||
// NSURL object is not nil, we create InputFile by ios::InputFile. The difference is
|
||||
// that ios::InputFile implements to read bytes from local real file for sending.
|
||||
GNCCore* core = GNCGetCore();
|
||||
NSURL* url = [core extractURLWithPayloadID:payload_id];
|
||||
if (url != nil) {
|
||||
return absl::make_unique<ios::InputFile>(url);
|
||||
} else {
|
||||
return absl::make_unique<shared::InputFile>(GetPayloadPath(payload_id), total_size);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(PayloadId payload_id) {
|
||||
return absl::make_unique<shared::OutputFile>(GetPayloadPath(payload_id));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
|
||||
const char* file, int line, LogMessage::Severity severity) {
|
||||
return absl::make_unique<ios::LogMessage>(file, line, severity);
|
||||
}
|
||||
|
||||
// Java-like Executors
|
||||
std::unique_ptr<SubmittableExecutor> ImplementationPlatform::CreateSingleThreadExecutor() {
|
||||
return std::make_unique<ios::SingleThreadExecutor>();
|
||||
}
|
||||
|
||||
std::unique_ptr<SubmittableExecutor> ImplementationPlatform::CreateMultiThreadExecutor(
|
||||
int max_concurrency) {
|
||||
return std::make_unique<ios::MultiThreadExecutor>(max_concurrency);
|
||||
}
|
||||
|
||||
std::unique_ptr<ScheduledExecutor> ImplementationPlatform::CreateScheduledExecutor() {
|
||||
return std::make_unique<ios::ScheduledExecutor>();
|
||||
}
|
||||
|
||||
// Mediums
|
||||
std::unique_ptr<BluetoothAdapter> ImplementationPlatform::CreateBluetoothAdapter() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothClassicMedium> ImplementationPlatform::CreateBluetoothClassicMedium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium(api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ServerSyncMedium> ImplementationPlatform::CreateServerSyncMedium() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() { return nullptr; }
|
||||
|
||||
std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
|
||||
return std::make_unique<ios::WifiLanMedium>();
|
||||
}
|
||||
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() { return nullptr; }
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
Reference in New Issue
Block a user