mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46: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
+2
-2
@@ -24,7 +24,7 @@ cc_library(
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
copts = ["-DCORE_ADAPTER_DLL"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//platform/impl/ios:__subpackages__",
|
||||
"//third_party/nearby_connections/windows:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -58,9 +58,9 @@ cc_library(
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
copts = ["-DCORE_ADAPTER_DLL"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//analytics:__subpackages__",
|
||||
"//core:__subpackages__",
|
||||
"//platform/impl/ios:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//absl/strings",
|
||||
|
||||
@@ -37,7 +37,6 @@ cc_library(
|
||||
],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//platform/base:__pkg__",
|
||||
"//platform/impl:__subpackages__",
|
||||
"//platform/public:__pkg__",
|
||||
|
||||
@@ -44,7 +44,6 @@ cc_library(
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
copts = ["-DCORE_ADAPTER_DLL"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//analytics:__subpackages__",
|
||||
"//third_party/nearby_connections/cpp/cal:__subpackages__",
|
||||
"//core:__subpackages__",
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# 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 = ["//visibility:public"])
|
||||
|
||||
objc_library(
|
||||
name = "Connections",
|
||||
srcs = [
|
||||
"Source/Internal/GNCAdvertiser.mm",
|
||||
"Source/Internal/GNCCore.mm",
|
||||
"Source/Internal/GNCCoreConnection.mm",
|
||||
"Source/Internal/GNCDiscoverer.mm",
|
||||
"Source/Internal/GNCPayload.m",
|
||||
"Source/Internal/GNCPayloadListener.mm",
|
||||
"Source/Internal/GNCUtils.mm",
|
||||
"Source/Internal/platform.mm",
|
||||
],
|
||||
hdrs = [
|
||||
"Source/GNCAdvertiser.h",
|
||||
"Source/GNCConnection.h",
|
||||
"Source/GNCConnections.h",
|
||||
"Source/GNCDiscoverer.h",
|
||||
"Source/GNCPayload.h",
|
||||
"Source/Internal/GNCCore.h",
|
||||
"Source/Internal/GNCCoreConnection.h",
|
||||
"Source/Internal/GNCPayload+Internal.h",
|
||||
"Source/Internal/GNCPayloadListener.h",
|
||||
"Source/Internal/GNCUtils.h",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"//absl/functional:bind_front",
|
||||
"//core",
|
||||
"//core:core_types",
|
||||
"//platform/impl/ios/Source/Mediums",
|
||||
"//platform/impl/ios/Source/Platform",
|
||||
"//platform/impl/ios/Source/Shared",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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 "GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This contains info about a discoverer endpoint intitiating a connection with an advertiser. */
|
||||
@protocol GNCAdvertiserConnectionInfo <NSObject>
|
||||
/** This is a human readable name of the discoverer. */
|
||||
@property(nonatomic, readonly, copy) NSString *name;
|
||||
/** This token can be used to verify the identity of the discoverer. */
|
||||
@property(nonatomic, readonly, copy) NSString *authToken;
|
||||
@end
|
||||
|
||||
/** This class contains success and failure handlers for the connection request. */
|
||||
@interface GNCConnectionResultHandlers : NSObject
|
||||
|
||||
/**
|
||||
* This factory method creates a pair of handlers for a successful or failed connection.
|
||||
*
|
||||
* @param successHandler This handler is called if both endpoints accept the connection.
|
||||
* A @c GNCConnection object is passed, meaning that the connection has
|
||||
* been established and you may start sending and receiving payloads.
|
||||
* @param failureHandler This handler is called if either endpoint rejects the connection.
|
||||
*/
|
||||
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler is called when a discoverer requests a connection with an advertiser. In
|
||||
* response, the advertiser should accept or reject via @c responseHandler.
|
||||
*
|
||||
* @param endpointId The ID of the endpoint.
|
||||
* @param connectionInfo Information about the discoverer.
|
||||
* @param responseHandler Handler for the connection response, which is either an acceptance or
|
||||
* rejection of the connection request.
|
||||
* @return Handlers for the final connection result. This will be called as soon as the final
|
||||
* connection result is known, when either side rejects or both sides accept.
|
||||
*/
|
||||
typedef GNCConnectionResultHandlers *_Nonnull (^GNCAdvertiserConnectionInitiationHandler)(
|
||||
GNCEndpointId endpointId, id<GNCAdvertiserConnectionInfo> connectionInfo,
|
||||
GNCConnectionResponseHandler responseHandler);
|
||||
|
||||
/**
|
||||
* An advertiser broadcasts a service that can be seen by discoverers, which can then make
|
||||
* requests to connect to it. Release the advertiser object to stop advertising.
|
||||
*/
|
||||
@interface GNCAdvertiser : NSObject
|
||||
|
||||
/**
|
||||
* Factory method that creates an advertiser.
|
||||
*
|
||||
* @param endpointInfo A data for endpoint info which contains readable name of this endpoint,
|
||||
* to be displayed on other endpoints.
|
||||
* @param serviceId A string that uniquely identifies the advertised service.
|
||||
* @param strategy The connection topology to use.
|
||||
* @param connectionInitiationHandler A handler that is called when a discoverer requests a
|
||||
* connection with this endpoint.
|
||||
*/
|
||||
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
|
||||
serviceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
connectionInitiationHandler:
|
||||
(GNCAdvertiserConnectionInitiationHandler)connectionInitiationHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,167 @@
|
||||
// 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
|
||||
|
||||
@class GNCBytesPayload, GNCStreamPayload, GNCFilePayload;
|
||||
|
||||
/** Response to a connection request. */
|
||||
typedef NS_ENUM(NSInteger, GNCConnectionResponse) {
|
||||
GNCConnectionResponseReject, // reject the connection request
|
||||
GNCConnectionResponseAccept, // accept the connection request
|
||||
};
|
||||
|
||||
/** Reason for a failed connection request. */
|
||||
typedef NS_ENUM(NSInteger, GNCConnectionFailure) {
|
||||
GNCConnectionFailureRejected, // an endpoint rejected the connection request
|
||||
GNCConnectionFailureUnknown, // there was an error while attempting to make the connection
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCConnectionFailure value. */
|
||||
typedef void (^GNCConnectionFailureHandler)(GNCConnectionFailure);
|
||||
|
||||
/** Reasons that a connection can be severed by either endpoint. */
|
||||
typedef NS_ENUM(NSInteger, GNCDisconnectedReason) {
|
||||
GNCDisconnectedReasonUnknown, // the endpoint can no longer be reached
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCDisconnectedReason value. */
|
||||
typedef void (^GNCDisconnectedHandler)(GNCDisconnectedReason);
|
||||
|
||||
/** Result of a payload transfer. */
|
||||
typedef NS_ENUM(NSInteger, GNCPayloadResult) {
|
||||
GNCPayloadResultSuccess, // Payload delivery was successful.
|
||||
GNCPayloadResultFailure, // An error occurred during payload delivery.
|
||||
GNCPayloadResultCanceled, // Payload delivery was canceled.
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCPayloadResult value. */
|
||||
typedef void (^GNCPayloadResultHandler)(GNCPayloadResult);
|
||||
|
||||
/** Connection topology. See https://developers.google.com/nearby/connections/strategies. */
|
||||
typedef NS_ENUM(NSInteger, GNCStrategy) {
|
||||
GNCStrategyCluster, // M-to-N
|
||||
GNCStrategyStar, // 1-to-N
|
||||
GNCStrategyPointToPoint, // 1-to-1
|
||||
};
|
||||
|
||||
/** Every endpoint has a unique identifier. */
|
||||
typedef NSString *GNCEndpointId;
|
||||
|
||||
/** This handler receives a Bytes payload. It is called when the payload data is fully received. */
|
||||
typedef void (^GNCBytesPayloadHandler)(GNCBytesPayload *payload);
|
||||
|
||||
/**
|
||||
* This handler receives a Stream payload, signifying the start of receipt of a stream. The payload
|
||||
* data should be read from the supplied input stream. The progress object can be used to monitor
|
||||
* progress or cancel the operation. This handler must return a completion handler, which is
|
||||
* called when the operation is finished.
|
||||
*/
|
||||
typedef GNCPayloadResultHandler _Nonnull (^GNCStreamPayloadHandler)(GNCStreamPayload *payload,
|
||||
NSProgress *progress);
|
||||
|
||||
/**
|
||||
* This handler receives a File payload, signifying the start of receipt of a file. The
|
||||
* progress object can be used to monitor progress or cancel the operation. This handler must
|
||||
* return a completion handler, which is called when the operation finishes successfully or if
|
||||
* there is an error. The file will be stored in a temporary location. If an error occurs or the
|
||||
* operation is canceled, the file will contain all data that was received. It is the client's
|
||||
* responsibility to delete the file when it is no longer needed.
|
||||
*/
|
||||
typedef GNCPayloadResultHandler _Nonnull (^GNCFilePayloadHandler)(GNCFilePayload *payload,
|
||||
NSProgress *progress);
|
||||
|
||||
/** This class contains optional handlers for a connection. */
|
||||
@interface GNCConnectionHandlers : NSObject
|
||||
|
||||
/**
|
||||
* This handler receives Bytes payloads. It is optional; apps that don't send and receive Bytes
|
||||
* payloads need not supply this handler.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCBytesPayloadHandler bytesPayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler receives a stream that delivers a payload in chunks. It is optional; apps that
|
||||
* don't send and receive Stream payloads need not supply this handler.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCStreamPayloadHandler streamPayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler receives a File payload. It is optional; apps that don't send and receive File
|
||||
* payloads need not supply this handler.
|
||||
* Note: File payloads are not yet supported.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCFilePayloadHandler filePayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler is called when the connection is ended, whether due to the endpoint disconnecting
|
||||
* or moving out of range. It is optional.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCDisconnectedHandler disconnectedHandler;
|
||||
|
||||
/**
|
||||
* This factory method lets you specify a subset of the connection handlers in a single expression.
|
||||
*
|
||||
* @param builderBlock Set up the handlers in this block.
|
||||
*/
|
||||
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers *))builderBlock;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This represents a connection with an endpoint. Use it to send payloads to the endpoint, and
|
||||
* release it to disconnect.
|
||||
*/
|
||||
@protocol GNCConnection <NSObject>
|
||||
|
||||
/**
|
||||
* Send a Bytes payload. A progress object is returned, which can be used to monitor
|
||||
* progress or cancel the operation. |completion| will be called when the operation completes
|
||||
* (in all cases, even if failed or was canceled).
|
||||
*/
|
||||
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
|
||||
/**
|
||||
* Send a Stream payload. A progress object is returned, which can be used to monitor
|
||||
* progress or cancel the operation. The stream data is read from the supplied NSInputStream.
|
||||
* |completion| will be called when the operation completes.
|
||||
*/
|
||||
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
|
||||
/**
|
||||
* Send a File payload. A progress object is returned, which can be used to monitor progress or
|
||||
* cancel the operation. |completion| will be called when the operation completes.
|
||||
* Note: File payloads are not yet supported.
|
||||
*/
|
||||
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler takes a @c GNCConnection object and returns a @c GNCConnectionHandlers
|
||||
* object containing the desired payload and connection-ended handlers.
|
||||
*/
|
||||
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandler)(id<GNCConnection> connection);
|
||||
|
||||
/**
|
||||
* This handler takes a response to a connection request. Pass @c GNCConnectionResponseAccept to
|
||||
* accept the request and @c GNCConnectionResponseReject to reject it.
|
||||
*/
|
||||
typedef void (^GNCConnectionResponseHandler)(GNCConnectionResponse response);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
// Umbrella header file for Nearby Connections library.
|
||||
|
||||
#import "GNCAdvertiser.h"
|
||||
#import "GNCConnection.h"
|
||||
#import "GNCDiscoverer.h"
|
||||
#import "GNCPayload.h"
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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 "GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This is info about an advertiser endpoint with which the discoverer has requested a connection.
|
||||
*/
|
||||
@protocol GNCDiscovererConnectionInfo <NSObject>
|
||||
/** This token can be used to verify the identity of the advertiser. */
|
||||
@property(nonatomic, readonly, copy) NSString *authToken;
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler is called to establish authorization with the advertiser. In response,
|
||||
* @c responseHandler should be called to accept or reject the connection.
|
||||
*
|
||||
* @param connectionInfo Information about the advertiser.
|
||||
* @param responseHandler Handler for the connection response, which is either an acceptance or
|
||||
* rejection of the connection request.
|
||||
* @return Handler for the connection if it was successful.
|
||||
*/
|
||||
typedef GNCConnectionHandler _Nonnull (^GNCDiscovererConnectionInitializationHandler)(
|
||||
id<GNCDiscovererConnectionInfo> connectionInfo, GNCConnectionResponseHandler responseHandler);
|
||||
|
||||
/**
|
||||
* This handler should be called to request a connection with an advertiser.
|
||||
*
|
||||
* @param name A human readable name of this endpoint, to be displayed on the other endpoint.
|
||||
* @param authorizationHandler This handler is called to establish authorization.
|
||||
* @param failureHandler This handler is called if there was an error making the connection.
|
||||
*/
|
||||
typedef void (^GNCConnectionRequester)(
|
||||
NSString *name, GNCDiscovererConnectionInitializationHandler connectionAuthorizationHandler,
|
||||
GNCConnectionFailureHandler failureHandler);
|
||||
|
||||
/** This contains info about a discovered advertiser endpoint. */
|
||||
@protocol GNCDiscoveredEndpointInfo <NSObject>
|
||||
/** This is a human readable name of the advertiser. */
|
||||
@property(nonatomic, readonly, copy) NSString *name;
|
||||
/** Call this block to request a connection with the advertiser. */
|
||||
@property(nonatomic, readonly) GNCConnectionRequester requestConnection;
|
||||
@end
|
||||
|
||||
/** This handler is called when a previously discovered advertiser endpoint is lost. */
|
||||
typedef void (^GNCEndpointLostHandler)(void);
|
||||
|
||||
/**
|
||||
* This handler is called when an advertiser endpoint is discovered.
|
||||
*
|
||||
* @param endpointId The ID of the endpoint.
|
||||
* @param connectionInfo Information about the endpoint.
|
||||
* @return Block that is called when the endpoint is lost.
|
||||
*/
|
||||
typedef GNCEndpointLostHandler _Nonnull (^GNCEndpointFoundHandler)(
|
||||
GNCEndpointId endpointId, id<GNCDiscoveredEndpointInfo> endpointInfo);
|
||||
|
||||
/**
|
||||
* A discoverer searches for endpoints advertising the specified service, and allows connection
|
||||
* requests to be sent to them. Release the discoverer object to stop discovering.
|
||||
*/
|
||||
@interface GNCDiscoverer : NSObject
|
||||
|
||||
/**
|
||||
* Factory method that creates a discoverer.
|
||||
*
|
||||
* @param serviceId A string that uniquely identifies the advertised service to search for.
|
||||
* @param strategy The connection topology to use.
|
||||
* @param endpointFoundHandler This handler is called when an endpoint advertising the service is
|
||||
* discovered.
|
||||
*/
|
||||
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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
|
||||
|
||||
/** This class encapsulates a Bytes payload. */
|
||||
@interface GNCBytesPayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** The content of the payload. */
|
||||
@property(nonatomic, readonly) NSData *bytes;
|
||||
|
||||
/**
|
||||
* Creates a Bytes payload object.
|
||||
* Note: To maximize performance, @c bytes is strongly referenced, not copied.
|
||||
*/
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes;
|
||||
|
||||
@end
|
||||
|
||||
/** This class encapsulates a Stream payload. */
|
||||
@interface GNCStreamPayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** The payload data is read from this input stream. */
|
||||
@property(nonatomic, readonly) NSInputStream *stream;
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream;
|
||||
|
||||
@end
|
||||
|
||||
/** This class encapsulates a File payload. */
|
||||
@interface GNCFilePayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** A URL that identifies the file. */
|
||||
@property(nonatomic, readonly, copy) NSURL *fileURL;
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -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
|
||||
@@ -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";
|
||||
@@ -0,0 +1,93 @@
|
||||
# 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 = "Platform",
|
||||
srcs = [
|
||||
"crypto.mm",
|
||||
"input_file.mm",
|
||||
"log_message.mm",
|
||||
"multi_thread_executor.mm",
|
||||
"scheduled_executor.mm",
|
||||
"utils.mm",
|
||||
"wifi_lan.mm",
|
||||
],
|
||||
hdrs = [
|
||||
"input_file.h",
|
||||
"log_message.h",
|
||||
"multi_thread_executor.h",
|
||||
"scheduled_executor.h",
|
||||
"single_thread_executor.h",
|
||||
"utils.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"CoreBluetooth",
|
||||
"CoreFoundation",
|
||||
],
|
||||
deps = [
|
||||
":Platform_cc",
|
||||
"//platform/api:platform",
|
||||
"//platform/api:types",
|
||||
"//platform/impl/ios/Source/Mediums",
|
||||
"//platform/impl/ios/Source/Shared",
|
||||
"//platform/impl/shared:file",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "Platform_cc",
|
||||
srcs = [
|
||||
"condition_variable.cc",
|
||||
"count_down_latch.cc",
|
||||
"system_clock.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"atomic_boolean.h",
|
||||
"atomic_uint32.h",
|
||||
"condition_variable.h",
|
||||
"count_down_latch.h",
|
||||
"mutex.h",
|
||||
],
|
||||
deps = [
|
||||
"//absl/strings:str_format",
|
||||
"//absl/synchronization",
|
||||
"//absl/time",
|
||||
"//platform/api:platform",
|
||||
"//platform/api:types",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "Platform_cc_test",
|
||||
srcs = [
|
||||
"atomic_boolean_test.cc",
|
||||
"atomic_uint32_test.cc",
|
||||
"condition_variable_test.cc",
|
||||
"count_down_latch_test.cc",
|
||||
"mutex_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":Platform_cc",
|
||||
"//testing/base/public:gunit_main",
|
||||
"//absl/synchronization",
|
||||
"//absl/time",
|
||||
"//thread/fiber",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
#define PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "platform/api/atomic_boolean.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete AtomicBoolean implementation.
|
||||
class AtomicBoolean : public api::AtomicBoolean {
|
||||
public:
|
||||
explicit AtomicBoolean(bool initial_value) : value_(initial_value) {}
|
||||
~AtomicBoolean() override = default;
|
||||
|
||||
AtomicBoolean(const AtomicBoolean&) = delete;
|
||||
AtomicBoolean& operator=(const AtomicBoolean&) = delete;
|
||||
|
||||
bool Get() const override { return value_.load(); }
|
||||
bool Set(bool value) override { return value_.exchange(value); }
|
||||
|
||||
private:
|
||||
std::atomic_bool value_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/atomic_boolean.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(AtomicBooleanTest, SetOnSameThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, MultipleSetGetOnSameThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(false);
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, SetOnNewThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
thread::Fiber f([&] { atomic_boolean_.Set(true); });
|
||||
f.Join();
|
||||
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, GetOnNewThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
thread::Fiber f([&] { EXPECT_EQ(true, atomic_boolean_.Get()); });
|
||||
f.Join();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
#define PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "platform/api/atomic_reference.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete AtomicUint32 implementation.
|
||||
class AtomicUint32 : public api::AtomicUint32 {
|
||||
public:
|
||||
explicit AtomicUint32(std::uint32_t initial_value) : value_(initial_value) {}
|
||||
~AtomicUint32() override = default;
|
||||
|
||||
AtomicUint32(const AtomicUint32&) = delete;
|
||||
AtomicUint32& operator=(const AtomicUint32&) = delete;
|
||||
|
||||
std::uint32_t Get() const override { return value_; }
|
||||
void Set(std::uint32_t value) override { value_ = value; }
|
||||
|
||||
private:
|
||||
std::atomic<std::uint32_t> value_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/atomic_uint32.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(AtomicUint32Test, GetOnSameThread) {
|
||||
std::uint32_t initial_value = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value};
|
||||
|
||||
EXPECT_EQ(initial_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, SetGetOnSameThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_value = 28;
|
||||
atomic_reference_.Set(new_value);
|
||||
EXPECT_EQ(new_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, SetOnNewThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_thread_value = 28;
|
||||
thread::Fiber f([&] { atomic_reference_.Set(new_thread_value); });
|
||||
f.Join();
|
||||
EXPECT_EQ(new_thread_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, GetOnNewThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_value = 28;
|
||||
atomic_reference_.Set(new_value);
|
||||
thread::Fiber f([&] { EXPECT_EQ(new_value, atomic_reference_.Get()); });
|
||||
f.Join();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/condition_variable.h"
|
||||
|
||||
#include "platform/impl/ios/Source/Platform/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
Exception ConditionVariable::Wait() {
|
||||
condition_variable_.Wait(mutex_);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
Exception ConditionVariable::Wait(absl::Duration timeout) {
|
||||
condition_variable_.WaitWithTimeout(mutex_, timeout);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void ConditionVariable::Notify() { condition_variable_.SignalAll(); }
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
#define PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "platform/api/condition_variable.h"
|
||||
#include "platform/impl/ios/Source/Platform/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete ConditionVariable implementation.
|
||||
class ConditionVariable : public api::ConditionVariable {
|
||||
public:
|
||||
explicit ConditionVariable(ios::Mutex* mutex) : mutex_(&mutex->mutex_) {}
|
||||
~ConditionVariable() override = default;
|
||||
|
||||
ConditionVariable(const ConditionVariable&) = delete;
|
||||
ConditionVariable& operator=(const ConditionVariable&) = delete;
|
||||
|
||||
Exception Wait() override;
|
||||
Exception Wait(absl::Duration timeout) override;
|
||||
void Notify() override;
|
||||
|
||||
private:
|
||||
absl::Mutex* mutex_;
|
||||
absl::CondVar condition_variable_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/condition_variable.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "platform/impl/ios/Source/Platform/mutex.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(ConditionVariableTest, CanCreate) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
}
|
||||
|
||||
TEST(ConditionVariableTest, CanWakeupWaiter) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
bool done = false;
|
||||
bool waiting = false;
|
||||
{
|
||||
thread::Fiber f([&cond, &mutex, &done, &waiting] {
|
||||
mutex.Lock();
|
||||
waiting = true;
|
||||
cond.Wait();
|
||||
waiting = false;
|
||||
done = true;
|
||||
mutex.Unlock();
|
||||
});
|
||||
while (true) {
|
||||
{
|
||||
mutex.Lock();
|
||||
if (waiting) {
|
||||
mutex.Unlock();
|
||||
break;
|
||||
}
|
||||
mutex.Unlock();
|
||||
}
|
||||
absl::SleepFor(absl::Milliseconds(100));
|
||||
}
|
||||
{
|
||||
mutex.Lock();
|
||||
cond.Notify();
|
||||
EXPECT_FALSE(done);
|
||||
mutex.Unlock();
|
||||
}
|
||||
f.Join();
|
||||
}
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(ConditionVariableTest, WaitTerminatesOnTimeoutWithoutNotify) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
mutex.Lock();
|
||||
|
||||
const absl::Duration kWaitTime = absl::Milliseconds(100);
|
||||
absl::Time start = absl::Now();
|
||||
cond.Wait(kWaitTime);
|
||||
absl::Duration duration = absl::Now() - start;
|
||||
EXPECT_GE(duration, kWaitTime);
|
||||
mutex.Unlock();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/count_down_latch.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
Exception CountDownLatch::Await() {
|
||||
absl::MutexLock lock(&mutex_, absl::Condition(IsZeroOrNegative, &count_));
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
ExceptionOr<bool> CountDownLatch::Await(absl::Duration timeout) {
|
||||
bool condition = mutex_.LockWhenWithTimeout(
|
||||
absl::Condition(IsZeroOrNegative, &count_), timeout);
|
||||
mutex_.Unlock();
|
||||
return ExceptionOr<bool>(condition);
|
||||
}
|
||||
|
||||
void CountDownLatch::CountDown() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
count_--;
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
#define PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "platform/api/count_down_latch.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete CountDownLatch implementation.
|
||||
class CountDownLatch : public api::CountDownLatch {
|
||||
public:
|
||||
explicit CountDownLatch(int count) : count_(count) {}
|
||||
~CountDownLatch() override = default;
|
||||
|
||||
CountDownLatch(const CountDownLatch&) = delete;
|
||||
CountDownLatch& operator=(const CountDownLatch&) = delete;
|
||||
|
||||
Exception Await() override;
|
||||
ExceptionOr<bool> Await(absl::Duration timeout) override;
|
||||
void CountDown() override;
|
||||
|
||||
private:
|
||||
static bool IsZeroOrNegative(int* count) { return 0 >= *count; }
|
||||
|
||||
absl::Mutex mutex_;
|
||||
int count_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/count_down_latch.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(CountDownLatchTest, LatchAwaitCanWait) {
|
||||
CountDownLatch latch(1);
|
||||
std::atomic_bool done = false;
|
||||
|
||||
thread::Fiber f([&done, &latch] {
|
||||
done = true;
|
||||
latch.CountDown();
|
||||
});
|
||||
f.Join();
|
||||
|
||||
latch.Await();
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, LatchExtraCountDownIgnored) {
|
||||
CountDownLatch latch(1);
|
||||
std::atomic_bool done = false;
|
||||
|
||||
thread::Fiber f([&done, &latch] {
|
||||
done = true;
|
||||
latch.CountDown();
|
||||
latch.CountDown();
|
||||
latch.CountDown();
|
||||
});
|
||||
f.Join();
|
||||
|
||||
latch.Await();
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, LatchAwaitWithTimeoutCanExpire) {
|
||||
CountDownLatch latch(1);
|
||||
|
||||
auto response = latch.Await(absl::Milliseconds(100));
|
||||
|
||||
EXPECT_TRUE(response.ok());
|
||||
EXPECT_FALSE(response.result());
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, InitialCountZero_AwaitDoesNotBlock) {
|
||||
CountDownLatch latch(0);
|
||||
|
||||
auto response = latch.Await();
|
||||
|
||||
EXPECT_TRUE(response.Ok());
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, InitialCountNegative_AwaitDoesNotBlock) {
|
||||
CountDownLatch latch(-1);
|
||||
|
||||
auto response = latch.Await();
|
||||
|
||||
EXPECT_TRUE(response.Ok());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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/crypto.h"
|
||||
|
||||
#import "third_party/absl/strings/string_view.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Shared/GNCUtils.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
void Crypto::Init() {}
|
||||
|
||||
ByteArray Crypto::Md5(absl::string_view input) {
|
||||
if (input.empty()) return ByteArray();
|
||||
|
||||
return ByteArrayFromNSData(GNCMd5String(ObjCStringFromCppString(input)));
|
||||
}
|
||||
|
||||
ByteArray Crypto::Sha256(absl::string_view input) {
|
||||
if (input.empty()) return ByteArray();
|
||||
|
||||
return ByteArrayFromNSData(GNCSha256String(ObjCStringFromCppString(input)));
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
#define PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "platform/api/input_file.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
/** This InputFile subclass takes input from an NSURL. */
|
||||
class InputFile : public api::InputFile {
|
||||
public:
|
||||
explicit InputFile(NSURL *nsURL);
|
||||
~InputFile() override = default;
|
||||
InputFile(InputFile &&) = default;
|
||||
InputFile &operator=(InputFile &&) = default;
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override;
|
||||
std::string GetFilePath() const override;
|
||||
std::int64_t GetTotalSize() const override;
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
NSURL *nsURL_;
|
||||
NSInputStream *nsStream_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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/Platform/input_file.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/base/exception.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
InputFile::InputFile(NSURL *nsURL) : nsURL_(nsURL) {
|
||||
std::string string = CppStringFromObjCString([nsURL_ absoluteString]);
|
||||
nsStream_ = [NSInputStream inputStreamWithURL:nsURL_];
|
||||
[nsStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[nsStream_ open];
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> InputFile::Read(std::int64_t size) {
|
||||
uint8_t *bytes_read = new uint8_t[size];
|
||||
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
|
||||
NSInteger numberOfBytesRead = [nsStream_ read:bytes_read 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:bytes_read
|
||||
length:numberOfBytesRead]));
|
||||
}
|
||||
|
||||
std::string InputFile::GetFilePath() const {
|
||||
return CppStringFromObjCString([nsURL_ absoluteString]);
|
||||
}
|
||||
|
||||
std::int64_t InputFile::GetTotalSize() const {
|
||||
NSNumber *fileSizeValue = nil;
|
||||
BOOL result = [nsURL_ getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
|
||||
if (result) {
|
||||
return fileSizeValue.longValue;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Exception InputFile::Close() {
|
||||
[nsStream_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
|
||||
#define PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
|
||||
|
||||
#include "base/check.h"
|
||||
#include "platform/api/log_message.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete LogMessage implementation
|
||||
class LogMessage : public api::LogMessage {
|
||||
public:
|
||||
LogMessage(const char* file, int line, Severity severity);
|
||||
~LogMessage() override = default;
|
||||
|
||||
LogMessage(const LogMessage&) = delete;
|
||||
LogMessage& operator=(const LogMessage&) = delete;
|
||||
|
||||
void Print(const char* format, ...) override;
|
||||
|
||||
std::ostream& Stream() override;
|
||||
|
||||
private:
|
||||
absl::LogStreamer log_streamer_;
|
||||
api::LogMessage::Severity severity_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // IPHONE_SHARED_NEARBY_CONNECTIONS_SOURCE_PLATFORM_LOG_MESSAGE_H_
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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/impl/ios/Source/Platform/log_message.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/log_message.h"
|
||||
#include "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
api::LogMessage::Severity gMinLogSeverity = api::LogMessage::Severity::kInfo;
|
||||
|
||||
GTMLoggerLevel ConvertSeverity(api::LogMessage::Severity severity) {
|
||||
switch (severity) {
|
||||
case api::LogMessage::Severity::kVerbose:
|
||||
return kGTMLoggerLevelDebug;
|
||||
case api::LogMessage::Severity::kInfo:
|
||||
return kGTMLoggerLevelInfo;
|
||||
case api::LogMessage::Severity::kWarning:
|
||||
return kGTMLoggerLevelInfo;
|
||||
case api::LogMessage::Severity::kError:
|
||||
return kGTMLoggerLevelError;
|
||||
case api::LogMessage::Severity::kFatal:
|
||||
return kGTMLoggerLevelAssert;
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage::LogMessage(const char* file, int line, Severity severity)
|
||||
: log_streamer_(ConvertSeverity(severity), file, line), severity_(severity) {}
|
||||
|
||||
void LogMessage::Print(const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
switch (ConvertSeverity(severity_)) {
|
||||
case kGTMLoggerLevelDebug:
|
||||
[[GTMLogger sharedLogger] logDebug:[NSString stringWithUTF8String:format], ap];
|
||||
break;
|
||||
case kGTMLoggerLevelInfo:
|
||||
[[GTMLogger sharedLogger] logInfo:[NSString stringWithUTF8String:format], ap];
|
||||
break;
|
||||
case kGTMLoggerLevelError:
|
||||
[[GTMLogger sharedLogger] logError:[NSString stringWithUTF8String:format], ap];
|
||||
break;
|
||||
case kGTMLoggerLevelAssert:
|
||||
[[GTMLogger sharedLogger] logAssert:[NSString stringWithUTF8String:format], ap];
|
||||
break;
|
||||
case kGTMLoggerLevelUnknown:
|
||||
// no-op
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/169292092): GTMLogger doesn't support stream. Temporarily use absl LogStreamer to make
|
||||
// build pass.
|
||||
std::ostream& LogMessage::Stream() { return log_streamer_.stream(); }
|
||||
|
||||
} // namespace ios
|
||||
|
||||
namespace api {
|
||||
|
||||
// static
|
||||
void LogMessage::SetMinLogSeverity(Severity severity) { ios::gMinLogSeverity = severity; }
|
||||
|
||||
// static
|
||||
bool LogMessage::ShouldCreateLogMessage(Severity severity) {
|
||||
// TODO(b/169292092): GTMLogger doesn't support stream which cause crash. Temporarily turn off
|
||||
// LogMessage.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
|
||||
|
||||
#include "platform/api/submittable_executor.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
class MultiThreadExecutor : public api::SubmittableExecutor {
|
||||
public:
|
||||
explicit MultiThreadExecutor(int max_concurrency);
|
||||
~MultiThreadExecutor() override = default;
|
||||
|
||||
MultiThreadExecutor(const MultiThreadExecutor&) = delete;
|
||||
MultiThreadExecutor& operator=(const MultiThreadExecutor&) = delete;
|
||||
|
||||
// api::SubmittableExecutor:
|
||||
void Shutdown() override;
|
||||
void Execute(Runnable&& runnable) override;
|
||||
bool DoSubmit(Runnable&& runnable) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ScheduledExecutor> scheduled_executor_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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/Platform/multi_thread_executor.h"
|
||||
|
||||
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
MultiThreadExecutor::MultiThreadExecutor(int max_concurrency) {
|
||||
scheduled_executor_ = std::make_unique<ScheduledExecutor>(max_concurrency);
|
||||
}
|
||||
|
||||
void MultiThreadExecutor::Shutdown() { scheduled_executor_->Shutdown(); }
|
||||
|
||||
void MultiThreadExecutor::Execute(Runnable&& runnable) {
|
||||
scheduled_executor_->Execute(std::move(runnable));
|
||||
}
|
||||
|
||||
bool MultiThreadExecutor::DoSubmit(Runnable&& runnable) {
|
||||
return scheduled_executor_->DoSubmit(std::move(runnable));
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_MUTEX_H_
|
||||
#define PLATFORM_IMPL_IOS_MUTEX_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "platform/api/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete Mutex implementation.
|
||||
class ABSL_LOCKABLE Mutex : public api::Mutex {
|
||||
public:
|
||||
explicit Mutex() {}
|
||||
~Mutex() override = default;
|
||||
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
|
||||
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
|
||||
mutex_.Lock();
|
||||
mutex_.ForgetDeadlockInfo();
|
||||
}
|
||||
void Unlock() ABSL_UNLOCK_FUNCTION() override { mutex_.Unlock(); }
|
||||
|
||||
private:
|
||||
friend class ConditionVariable;
|
||||
absl::Mutex mutex_;
|
||||
};
|
||||
|
||||
class ABSL_LOCKABLE RecursiveMutex : public api::Mutex {
|
||||
public:
|
||||
RecursiveMutex() = default;
|
||||
~RecursiveMutex() override = default;
|
||||
|
||||
RecursiveMutex(RecursiveMutex&&) = delete;
|
||||
RecursiveMutex& operator=(RecursiveMutex&&) = delete;
|
||||
|
||||
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
|
||||
intptr_t thread_id = ThreadId();
|
||||
if (thread_id_.load(std::memory_order_acquire) != thread_id) {
|
||||
mutex_.Lock();
|
||||
thread_id_.store(thread_id, std::memory_order_release);
|
||||
}
|
||||
++count_;
|
||||
}
|
||||
|
||||
void Unlock() ABSL_UNLOCK_FUNCTION() override {
|
||||
if (--count_ == 0) {
|
||||
thread_id_.store(0, std::memory_order_release);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static inline intptr_t ThreadId() {
|
||||
ABSL_CONST_INIT thread_local int per_thread = 0;
|
||||
return reinterpret_cast<intptr_t>(&per_thread);
|
||||
}
|
||||
|
||||
std::atomic<intptr_t> thread_id_{0};
|
||||
int count_{0};
|
||||
absl::Mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_MUTEX_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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 "platform/impl/ios/Source/Platform/mutex.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
static const absl::Duration kTimeToWait = absl::Milliseconds(500);
|
||||
|
||||
TEST(MutexTest, LockOnce_UnlockOnce) {
|
||||
Mutex test_mutex_1{};
|
||||
test_mutex_1.Lock();
|
||||
test_mutex_1.Unlock();
|
||||
|
||||
RecursiveMutex test_mutex_2;
|
||||
test_mutex_2.Lock();
|
||||
test_mutex_2.Unlock();
|
||||
}
|
||||
|
||||
TEST(MutexTest, BasicLockingWorks) {
|
||||
absl::Notification lock_obtained;
|
||||
Mutex test_mutex{};
|
||||
test_mutex.Lock();
|
||||
thread::Fiber f([&test_mutex, &lock_obtained] {
|
||||
test_mutex.Lock();
|
||||
test_mutex.Unlock();
|
||||
lock_obtained.Notify();
|
||||
});
|
||||
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
test_mutex.Unlock();
|
||||
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
f.Join();
|
||||
}
|
||||
|
||||
TEST(MutexTest, RecursiveLockingWorks) {
|
||||
absl::Notification lock_obtained;
|
||||
RecursiveMutex test_mutex;
|
||||
test_mutex.Lock();
|
||||
thread::Fiber f([&test_mutex, &lock_obtained] {
|
||||
test_mutex.Lock();
|
||||
test_mutex.Unlock();
|
||||
lock_obtained.Notify();
|
||||
});
|
||||
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
test_mutex.Unlock();
|
||||
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
f.Join();
|
||||
}
|
||||
|
||||
TEST(MutexTest, RecursiveLockingForNestedWorks) {
|
||||
absl::Notification lock_obtained;
|
||||
RecursiveMutex test_mutex;
|
||||
test_mutex.Lock();
|
||||
thread::Fiber f([&test_mutex, &lock_obtained]()
|
||||
ABSL_NO_THREAD_SAFETY_ANALYSIS {
|
||||
test_mutex.Lock();
|
||||
test_mutex.Lock();
|
||||
test_mutex.Unlock();
|
||||
test_mutex.Unlock();
|
||||
lock_obtained.Notify();
|
||||
});
|
||||
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
test_mutex.Unlock();
|
||||
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
|
||||
f.Join();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "platform/api/scheduled_executor.h"
|
||||
#include "platform/base/runnable.h"
|
||||
|
||||
/**
|
||||
* The impl class is an Obj-C class so that
|
||||
* (a) the dispatch block can strongly retain it, and
|
||||
* (b) for ease of declaring an atomic property.
|
||||
*/
|
||||
@interface GNCOperationQueueImpl : NSObject
|
||||
@property(nonatomic) NSOperationQueue* queue;
|
||||
@property(atomic) BOOL shuttingDown;
|
||||
@end
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete ScheduledExecutor implementation.
|
||||
class ScheduledExecutor : public api::ScheduledExecutor {
|
||||
public:
|
||||
// The max_concurrency = 1 for default constructor.
|
||||
ScheduledExecutor();
|
||||
explicit ScheduledExecutor(int max_concurrency);
|
||||
~ScheduledExecutor() override;
|
||||
|
||||
ScheduledExecutor(const ScheduledExecutor&) = delete;
|
||||
ScheduledExecutor& operator=(const ScheduledExecutor&) = delete;
|
||||
|
||||
// api::ScheduledExecutor:
|
||||
void Shutdown() override;
|
||||
std::shared_ptr<api::Cancelable> Schedule(Runnable&& runnable, absl::Duration duration) override;
|
||||
void Execute(Runnable&& runnable) override;
|
||||
|
||||
bool DoSubmit(Runnable&& runnable);
|
||||
|
||||
private:
|
||||
void Shutdown(std::int64_t timeout_millis);
|
||||
|
||||
GNCOperationQueueImpl* impl_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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/Platform/scheduled_executor.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "third_party/absl/time/time.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/atomic_boolean.h"
|
||||
|
||||
// This wraps the C++ Runnable in an Obj-C object for memory management. It is retained by the
|
||||
// dispatch block below, and deleted when the block is released.
|
||||
@interface GNCRunnableWrapper : NSObject {
|
||||
@public
|
||||
location::nearby::Runnable _runnable;
|
||||
std::unique_ptr<location::nearby::ios::AtomicBoolean> _canceled;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GNCRunnableWrapper
|
||||
|
||||
+ (instancetype)wrapperWithRunnable:(location::nearby::Runnable)runnable {
|
||||
GNCRunnableWrapper *wrapper = [[GNCRunnableWrapper alloc] init];
|
||||
wrapper->_runnable = runnable;
|
||||
wrapper->_canceled = std::make_unique<location::nearby::ios::AtomicBoolean>(false);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCOperationQueueImpl
|
||||
|
||||
+ (instancetype)implWithMaxConcurrency:(int)maxConcurrency {
|
||||
GNCOperationQueueImpl *impl = [[GNCOperationQueueImpl alloc] init];
|
||||
impl.queue = [[NSOperationQueue alloc] init];
|
||||
impl.queue.maxConcurrentOperationCount = maxConcurrency;
|
||||
return impl;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
static const std::int64_t kExecutorShutdownDefaultTimeout = 500; // 0.5 seconds
|
||||
|
||||
// This Cancelable references a Runnable and a cancel method that sets its canceled boolean to true.
|
||||
class CancelableForRunnable : public api::Cancelable {
|
||||
public:
|
||||
explicit CancelableForRunnable(GNCRunnableWrapper *runnable) : runnable_(runnable) {}
|
||||
CancelableForRunnable() = default;
|
||||
~CancelableForRunnable() override = default;
|
||||
CancelableForRunnable(const CancelableForRunnable &) = delete;
|
||||
CancelableForRunnable &operator=(const CancelableForRunnable &) = delete;
|
||||
|
||||
// api::Cancelable:
|
||||
bool Cancel() override {
|
||||
runnable_->_canceled->Set(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
GNCRunnableWrapper *runnable_;
|
||||
};
|
||||
|
||||
ScheduledExecutor::ScheduledExecutor() { impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:1]; }
|
||||
|
||||
ScheduledExecutor::ScheduledExecutor(int max_concurrency) {
|
||||
impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:max_concurrency];
|
||||
}
|
||||
|
||||
ScheduledExecutor::~ScheduledExecutor() { impl_ = nil; }
|
||||
|
||||
void ScheduledExecutor::Shutdown() { Shutdown(kExecutorShutdownDefaultTimeout); }
|
||||
|
||||
std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable,
|
||||
absl::Duration duration) {
|
||||
if (impl_.shuttingDown) return std::shared_ptr<api::Cancelable>(nullptr);
|
||||
|
||||
// Wrap the runnable in an Obj-C object so it can be referenced by the delayed block.
|
||||
GNCRunnableWrapper *wrapper = [GNCRunnableWrapper wrapperWithRunnable:std::move(runnable)];
|
||||
CancelableForRunnable *cancelable = new CancelableForRunnable(wrapper);
|
||||
GNCOperationQueueImpl *impl = impl_; // don't capture |this|
|
||||
dispatch_after(
|
||||
dispatch_time(DISPATCH_TIME_NOW, absl::ToInt64Milliseconds(duration) * NSEC_PER_MSEC),
|
||||
dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0), ^{
|
||||
[impl.queue addOperationWithBlock:^{
|
||||
// Execute the runnable only if the executor is not shutting down, and the runnable isn't
|
||||
// canceled.
|
||||
// Warning: This block should reference only Obj-C objects, and never C++ objects.
|
||||
if (!impl.shuttingDown && !wrapper->_canceled->Get()) {
|
||||
wrapper->_runnable();
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
return std::shared_ptr<api::Cancelable>(cancelable);
|
||||
}
|
||||
|
||||
void ScheduledExecutor::Execute(Runnable &&runnable) {
|
||||
DoSubmit(std::move(runnable));
|
||||
}
|
||||
|
||||
bool ScheduledExecutor::DoSubmit(Runnable &&runnable) {
|
||||
if (impl_.shuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Submit the runnable to the queue.
|
||||
Runnable local_runnable = std::move(runnable);
|
||||
[impl_.queue addOperationWithBlock:^{
|
||||
local_runnable();
|
||||
}];
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScheduledExecutor::Shutdown(std::int64_t timeout_millis) {
|
||||
// Prevent new/delayed operations from being queued/executed.
|
||||
impl_.shuttingDown = YES;
|
||||
|
||||
// Block until either (a) all currently executing operations finish, or (b) the timeout expires.
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0), ^{
|
||||
[impl_.queue waitUntilAllOperationsAreFinished];
|
||||
});
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, timeout_millis * NSEC_PER_MSEC));
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/multi_thread_executor.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
class SingleThreadExecutor : public MultiThreadExecutor {
|
||||
public:
|
||||
SingleThreadExecutor() : MultiThreadExecutor(1) {}
|
||||
~SingleThreadExecutor() override = default;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 "platform/api/system_clock.h"
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
void SystemClock::Init() {}
|
||||
|
||||
absl::Time SystemClock::ElapsedRealtime() { return absl::Now(); }
|
||||
|
||||
// TODO(b/169292092): Check the iOS primitive implementation for SystemClock.
|
||||
Exception SystemClock::Sleep(absl::Duration duration) {
|
||||
absl::SleepFor(duration);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "platform/base/byte_array.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
/// Converts an Obj-C BOOL to a C++ bool.
|
||||
bool CppBoolFromObjCBool(BOOL b);
|
||||
|
||||
/// Converts NSNumber to a char.
|
||||
char CharFromNSNumber(NSNumber *n);
|
||||
|
||||
/// Converts a C++ string to an Obj-C string.
|
||||
NSString *ObjCStringFromCppString(absl::string_view s);
|
||||
|
||||
/// Converts an Obj-C string to a C++ string.
|
||||
std::string CppStringFromObjCString(NSString *s);
|
||||
|
||||
/// Converts ByteArray to NSData.
|
||||
NSData *NSDataFromByteArray(ByteArray byteArray);
|
||||
|
||||
/// Converts NSData to ByteArray.
|
||||
ByteArray ByteArrayFromNSData(NSData *data);
|
||||
|
||||
/// Converts NSUUID to a C++ string (representing a UUID).
|
||||
std::string UUIDStringFromNSUUID(NSUUID *uuid);
|
||||
|
||||
/// Converts a C++ string (representing a Bluetooth UUID) to CBUUID.
|
||||
CBUUID *CBUUIDFromBluetoothUUIDString(absl::string_view bluetoothUUID);
|
||||
|
||||
/// Converts CBUUID to a C++ string (representing a Bluetooth UUID).
|
||||
std::string BluetoothUUIDStringFromCBUUID(CBUUID *bluetoothUUID);
|
||||
|
||||
/// Converts a C++ set of strings (representing Bluetooth UUIDs) to an NSSet of CBUUID.
|
||||
NSSet<CBUUID *> *CBUUIDSetFromBluetoothUUIDStringSet(const std::set<std::string> &bluetoothUUIDSet);
|
||||
|
||||
/// Converts an NSSet of CBUUID to a C++ set of strings (representing Bluetooth UUIDs).
|
||||
std::set<std::string> BluetoothUUIDStringSetFromCBUUIDSet(NSSet<CBUUID *> *bluetoothUUIDSet);
|
||||
|
||||
/// Converts a C++ TxtRecord to an Obj-C TxtRecord.
|
||||
NSDictionary<NSString *, NSData *> *NSDictionaryFromCppTxtRecords(
|
||||
const absl::flat_hash_map<std::string, std::string> &txt_records);
|
||||
|
||||
/// Converts an Obj-C TxtRecord to a C++ TxtRecord.
|
||||
absl::flat_hash_map<std::string, std::string> AbslHashMapFromObjCTxtRecords(
|
||||
NSDictionary<NSString *, NSData *> *txtRecords);
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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/Platform/utils.h"
|
||||
|
||||
#import "third_party/absl/container/flat_hash_map.h"
|
||||
#include "third_party/absl/strings/string_view.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
bool CppBoolFromObjCBool(BOOL b) { return b ? true : false; }
|
||||
|
||||
char CharFromNSNumber(NSNumber* n) { return n.charValue; }
|
||||
|
||||
NSString* ObjCStringFromCppString(absl::string_view s) {
|
||||
return [NSString stringWithUTF8String:s.data()];
|
||||
}
|
||||
|
||||
std::string CppStringFromObjCString(NSString* s) {
|
||||
return std::string([s UTF8String], [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
NSData* NSDataFromByteArray(ByteArray byteArray) {
|
||||
return [NSData dataWithBytes:byteArray.data() length:byteArray.size()];
|
||||
}
|
||||
|
||||
ByteArray ByteArrayFromNSData(NSData* data) {
|
||||
return ByteArray((const char*)data.bytes, data.length);
|
||||
}
|
||||
|
||||
std::string UUIDStringFromNSUUID(NSUUID* uuid) { return CppStringFromObjCString(uuid.UUIDString); }
|
||||
|
||||
CBUUID* CBUUIDFromBluetoothUUIDString(absl::string_view bluetoothUUID) {
|
||||
return [CBUUID UUIDWithString:ObjCStringFromCppString(bluetoothUUID)];
|
||||
}
|
||||
|
||||
std::string BluetoothUUIDStringFromCBUUID(CBUUID* bluetoothUUID) {
|
||||
return CppStringFromObjCString(bluetoothUUID.UUIDString);
|
||||
}
|
||||
|
||||
NSSet<CBUUID*>* CBUUIDSetFromBluetoothUUIDStringSet(const std::set<std::string>& bluetoothUUIDSet) {
|
||||
NSMutableSet<CBUUID*>* objcBluetoothUUIDSet = [NSMutableSet set];
|
||||
for (std::set<std::string>::const_iterator it = bluetoothUUIDSet.begin();
|
||||
it != bluetoothUUIDSet.end(); ++it) {
|
||||
[objcBluetoothUUIDSet addObject:CBUUIDFromBluetoothUUIDString(*it)];
|
||||
}
|
||||
return objcBluetoothUUIDSet;
|
||||
}
|
||||
|
||||
std::set<std::string> BluetoothUUIDStringSetFromCBUUIDSet(NSSet<CBUUID*>* bluetoothUUIDSet) {
|
||||
std::set<std::string> cppBluetoothUUIDSet;
|
||||
for (CBUUID* bluetoothUUID in bluetoothUUIDSet) {
|
||||
cppBluetoothUUIDSet.insert(BluetoothUUIDStringFromCBUUID(bluetoothUUID));
|
||||
}
|
||||
return cppBluetoothUUIDSet;
|
||||
}
|
||||
|
||||
NSDictionary<NSString*, NSData*>* NSDictionaryFromCppTxtRecords(
|
||||
const absl::flat_hash_map<std::string, std::string>& txt_records) {
|
||||
NSMutableArray<NSString*>* keyArray = [[NSMutableArray alloc] init];
|
||||
NSMutableArray<NSData*>* valueArray = [[NSMutableArray alloc] init];
|
||||
for (auto it = txt_records.begin(); it != txt_records.end(); it++) {
|
||||
NSString* key = @(it->first.c_str());
|
||||
NSString* value = @(it->second.c_str());
|
||||
[keyArray addObject:key];
|
||||
[valueArray addObject:[value dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
|
||||
NSDictionary<NSString*, NSData*>* dict = [[NSDictionary alloc] initWithObjects:valueArray
|
||||
forKeys:keyArray];
|
||||
return dict;
|
||||
}
|
||||
|
||||
absl::flat_hash_map<std::string, std::string> AbslHashMapFromObjCTxtRecords(
|
||||
NSDictionary<NSString*, NSData*>* txtRecords) {
|
||||
absl::flat_hash_map<std::string, std::string> txt_record;
|
||||
|
||||
for (NSString* key in txtRecords) {
|
||||
NSString* value = [[NSString alloc] initWithData:[txtRecords objectForKey:key]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
txt_record.insert({CppStringFromObjCString(key), CppStringFromObjCString(value)});
|
||||
}
|
||||
return txt_record;
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_WIFI_LAN_H_
|
||||
#define PLATFORM_IMPL_IOS_WIFI_LAN_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "platform/api/wifi_lan.h"
|
||||
#include "platform/base/nsd_service_info.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h" // IWYU pragma: export
|
||||
|
||||
@class GNCMBonjourBrowser;
|
||||
@class GNCMBonjourService;
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
/** InputStream that reads from GNCMConnection. */
|
||||
class WifiLanInputStream : public InputStream {
|
||||
public:
|
||||
WifiLanInputStream();
|
||||
~WifiLanInputStream() override;
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override;
|
||||
Exception Close() override;
|
||||
|
||||
GNCMConnectionHandlers* GetConnectionHandlers() { return connectionHandlers_; }
|
||||
|
||||
private:
|
||||
GNCMConnectionHandlers* connectionHandlers_;
|
||||
NSMutableArray<NSData*>* newDataPackets_;
|
||||
NSMutableData* accumulatedData_;
|
||||
NSCondition* condition_;
|
||||
};
|
||||
|
||||
/** OutputStream that writes to GNCMConnection. */
|
||||
class WifiLanOutputStream : public OutputStream {
|
||||
public:
|
||||
explicit WifiLanOutputStream(id<GNCMConnection> connection)
|
||||
: connection_(connection), condition_([[NSCondition alloc] init]) {}
|
||||
~WifiLanOutputStream() override;
|
||||
|
||||
Exception Write(const ByteArray& data) override;
|
||||
Exception Flush() override;
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
id<GNCMConnection> connection_;
|
||||
NSCondition* condition_;
|
||||
};
|
||||
|
||||
/** Concrete WifiLanSocket implementation. */
|
||||
class WifiLanSocket : public api::WifiLanSocket {
|
||||
public:
|
||||
WifiLanSocket() = default;
|
||||
explicit WifiLanSocket(id<GNCMConnection> connection);
|
||||
~WifiLanSocket() override;
|
||||
|
||||
// api::WifiLanSocket:
|
||||
InputStream& GetInputStream() override { return *input_stream_; }
|
||||
OutputStream& GetOutputStream() override { return *output_stream_; }
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
mutable absl::Mutex mutex_;
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
std::unique_ptr<WifiLanInputStream> input_stream_;
|
||||
std::unique_ptr<WifiLanOutputStream> output_stream_;
|
||||
};
|
||||
|
||||
/** Concrete WifiLanServerSocket implementation. */
|
||||
class WifiLanServerSocket : public api::WifiLanServerSocket {
|
||||
public:
|
||||
static std::string GetName(const std::string& ip_address, int port);
|
||||
|
||||
~WifiLanServerSocket() override;
|
||||
|
||||
// api::WifiLanServerSocket:
|
||||
std::string GetIPAddress() const override ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return ip_address_;
|
||||
}
|
||||
void SetIPAddress(const std::string& ip_address) ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
ip_address_ = ip_address;
|
||||
}
|
||||
int GetPort() const override ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return port_;
|
||||
}
|
||||
void SetPort(int port) ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
port_ = port;
|
||||
}
|
||||
std::unique_ptr<api::WifiLanSocket> Accept() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool Connect(std::unique_ptr<WifiLanSocket> socket) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
void SetCloseNotifier(std::function<void()> notifier) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
mutable absl::Mutex mutex_;
|
||||
std::string ip_address_ ABSL_GUARDED_BY(mutex_);
|
||||
int port_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::CondVar cond_;
|
||||
absl::flat_hash_set<std::unique_ptr<WifiLanSocket>> pending_sockets_ ABSL_GUARDED_BY(mutex_);
|
||||
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
/** Concrete WifiLanMedium implementation. */
|
||||
class WifiLanMedium : public api::WifiLanMedium {
|
||||
public:
|
||||
WifiLanMedium() = default;
|
||||
~WifiLanMedium() override;
|
||||
|
||||
WifiLanMedium(const WifiLanMedium&) = delete;
|
||||
WifiLanMedium& operator=(const WifiLanMedium&) = delete;
|
||||
|
||||
// api::WifiLanMedium:
|
||||
bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool StartDiscovery(const std::string& service_type, DiscoveredServiceCallback callback) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
bool StopDiscovery(const std::string& service_type) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> ConnectToService(
|
||||
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override;
|
||||
std::unique_ptr<api::WifiLanSocket> ConnectToService(
|
||||
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override;
|
||||
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
struct AdvertisingInfo {
|
||||
bool Empty() const { return services.empty(); }
|
||||
void Clear() { services.clear(); }
|
||||
void Add(const std::string& service_type, GNCMBonjourService* service) {
|
||||
services.insert({service_type, service});
|
||||
}
|
||||
void Remove(const std::string& service_type) { services.erase(service_type); }
|
||||
bool Existed(const std::string& service_type) const { return services.contains(service_type); }
|
||||
|
||||
absl::flat_hash_map<std::string, GNCMBonjourService*> services;
|
||||
};
|
||||
struct DiscoveringInfo {
|
||||
bool Empty() const { return services.empty(); }
|
||||
void Clear() { services.clear(); }
|
||||
void Add(const std::string& service_type, GNCMBonjourBrowser* browser) {
|
||||
services.insert({service_type, browser});
|
||||
}
|
||||
void Remove(const std::string& service_type) { services.erase(service_type); }
|
||||
bool Existed(const std::string& service_type) const { return services.contains(service_type); }
|
||||
|
||||
absl::flat_hash_map<std::string, GNCMBonjourBrowser*> services;
|
||||
};
|
||||
|
||||
std::string GetFakeIPAddress() const;
|
||||
int GetFakePort() const;
|
||||
|
||||
absl::Mutex mutex_;
|
||||
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
|
||||
int requesting_port_ = 0;
|
||||
DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::flat_hash_map<std::string, WifiLanServerSocket*> server_sockets_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::flat_hash_map<std::string, GNCMConnectionRequester> connection_requesters_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_WIFI_LAN_H_
|
||||
@@ -0,0 +1,497 @@
|
||||
// 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/Platform/wifi_lan.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "third_party/absl/container/flat_hash_map.h"
|
||||
#include "third_party/absl/container/internal/common.h"
|
||||
#include "third_party/absl/memory/memory.h"
|
||||
#include "third_party/absl/strings/str_cat.h"
|
||||
#include "third_party/absl/strings/str_format.h"
|
||||
#include "third_party/absl/synchronization/mutex.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/wifi_lan.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/cancellation_flag.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/exception.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/nsd_service_info.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/prng.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/GNCMBonjourBrowser.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
/** WifiLanInputStream implementation. */
|
||||
WifiLanInputStream::WifiLanInputStream()
|
||||
: newDataPackets_([NSMutableArray array]),
|
||||
accumulatedData_([NSMutableData data]),
|
||||
condition_([[NSCondition alloc] init]) {
|
||||
// Create the handlers of incoming data from the remote endpoint.
|
||||
connectionHandlers_ = [GNCMConnectionHandlers
|
||||
payloadHandler:^(NSData* data) {
|
||||
[condition_ lock];
|
||||
// Add the incoming data to the data packet array to be processed in Read() below.
|
||||
[newDataPackets_ addObject:data];
|
||||
[condition_ signal];
|
||||
[condition_ unlock];
|
||||
}
|
||||
disconnectedHandler:^{
|
||||
[condition_ lock];
|
||||
// Release the data packet array, meaning the stream has been closed or severed.
|
||||
newDataPackets_ = nil;
|
||||
[condition_ signal];
|
||||
[condition_ unlock];
|
||||
}];
|
||||
}
|
||||
|
||||
WifiLanInputStream::~WifiLanInputStream() {
|
||||
NSCAssert(!newDataPackets_, @"WifiLanInputStream not closed before destruction");
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> WifiLanInputStream::Read(std::int64_t size) {
|
||||
// Block until either (a) the connection has been closed, or (b) enough data to return.
|
||||
NSData* dataToReturn;
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
// Add the packet data to the accumulated data.
|
||||
for (NSData* data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
}
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
}
|
||||
|
||||
if ((size == -1) && (accumulatedData_.length > 0)) {
|
||||
// Return all of the data.
|
||||
dataToReturn = accumulatedData_;
|
||||
accumulatedData_ = [NSMutableData data];
|
||||
break;
|
||||
} else if (accumulatedData_.length > 0) {
|
||||
// Return up to |size| bytes of the data.
|
||||
std::int64_t sizeToReturn = accumulatedData_.length < size ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
dataToReturn = [accumulatedData_ subdataWithRange:range];
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
break;
|
||||
}
|
||||
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (dataToReturn) {
|
||||
GTMLoggerInfo(@"[NEARBY] Input stream: Received data of size: %lu",
|
||||
(unsigned long)dataToReturn.length);
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData(dataToReturn));
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
}
|
||||
|
||||
Exception WifiLanInputStream::Close() {
|
||||
// Unblock pending read operation.
|
||||
[condition_ lock];
|
||||
newDataPackets_ = nil;
|
||||
[condition_ signal];
|
||||
[condition_ unlock];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
/** WifiLanOutputStream implementation. */
|
||||
WifiLanOutputStream::~WifiLanOutputStream() {
|
||||
NSCAssert(!connection_, @"WifiLanOutputStream not closed before destruction");
|
||||
}
|
||||
|
||||
Exception WifiLanOutputStream::Write(const ByteArray& data) {
|
||||
[condition_ lock];
|
||||
GTMLoggerDebug(@"[NEARBY] Sending data of size: %lu",
|
||||
(unsigned long)NSDataFromByteArray(data).length);
|
||||
|
||||
NSMutableData* packet = [NSMutableData dataWithData:NSDataFromByteArray(data)];
|
||||
|
||||
// Send the data, blocking until the completion handler is called.
|
||||
__block GNCMPayloadResult sendResult = GNCMPayloadResultFailure;
|
||||
__block bool isComplete = NO;
|
||||
NSCondition* condition = condition_; // don't capture |this| in completion
|
||||
// Check if connection_ is nil, then just don't wait and return as failure.
|
||||
if (connection_ != nil) {
|
||||
[connection_ sendData:packet
|
||||
progressHandler:^(size_t count) {
|
||||
}
|
||||
completion:^(GNCMPayloadResult result) {
|
||||
// Make sure we haven't already reported completion before. This prevents a crash
|
||||
// where we try leaving a dispatch group more times than we entered it.
|
||||
// b/79095653.
|
||||
if (isComplete) {
|
||||
return;
|
||||
}
|
||||
isComplete = YES;
|
||||
sendResult = result;
|
||||
[condition lock];
|
||||
[condition signal];
|
||||
[condition unlock];
|
||||
}];
|
||||
[condition_ wait];
|
||||
[condition_ unlock];
|
||||
} else {
|
||||
sendResult = GNCMPayloadResultFailure;
|
||||
[condition_ unlock];
|
||||
}
|
||||
|
||||
if (sendResult == GNCMPayloadResultSuccess) {
|
||||
return {Exception::kSuccess};
|
||||
} else {
|
||||
return {Exception::kIo};
|
||||
}
|
||||
}
|
||||
|
||||
Exception WifiLanOutputStream::Flush() {
|
||||
// The Write() function block until the data is received by the remote endpoint, so there's
|
||||
// nothing to do here.
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
Exception WifiLanOutputStream::Close() {
|
||||
// Unblock pending write operation.
|
||||
[condition_ lock];
|
||||
connection_ = nil;
|
||||
[condition_ signal];
|
||||
[condition_ unlock];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
/** WifiLanSocket implementation. */
|
||||
WifiLanSocket::WifiLanSocket(id<GNCMConnection> connection)
|
||||
: input_stream_(new WifiLanInputStream()),
|
||||
output_stream_(new WifiLanOutputStream(connection)) {}
|
||||
|
||||
WifiLanSocket::~WifiLanSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
bool WifiLanSocket::IsClosed() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return closed_;
|
||||
}
|
||||
|
||||
Exception WifiLanSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void WifiLanSocket::DoClose() {
|
||||
if (!closed_) {
|
||||
input_stream_->Close();
|
||||
output_stream_->Close();
|
||||
closed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** WifiLanServerSocket implementation. */
|
||||
std::string WifiLanServerSocket::GetName(const std::string& ip_address, int port) {
|
||||
std::string dot_delimited_string;
|
||||
if (!ip_address.empty()) {
|
||||
for (auto byte : ip_address) {
|
||||
if (!dot_delimited_string.empty()) absl::StrAppend(&dot_delimited_string, ".");
|
||||
absl::StrAppend(&dot_delimited_string, absl::StrFormat("%d", byte));
|
||||
}
|
||||
}
|
||||
std::string out = absl::StrCat(dot_delimited_string, ":", port);
|
||||
return out;
|
||||
}
|
||||
|
||||
WifiLanServerSocket::~WifiLanServerSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
while (!closed_ && pending_sockets_.empty()) {
|
||||
cond_.Wait(&mutex_);
|
||||
}
|
||||
// Return early if closed.
|
||||
if (closed_) return {};
|
||||
|
||||
auto remote_socket = std::move(pending_sockets_.extract(pending_sockets_.begin()).value());
|
||||
return std::move(remote_socket);
|
||||
}
|
||||
|
||||
bool WifiLanServerSocket::Connect(std::unique_ptr<WifiLanSocket> socket) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (closed_) {
|
||||
return false;
|
||||
}
|
||||
// add client socket to the pending list
|
||||
pending_sockets_.insert(std::move(socket));
|
||||
cond_.SignalAll();
|
||||
if (closed_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
close_notifier_ = std::move(notifier);
|
||||
}
|
||||
|
||||
Exception WifiLanServerSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return DoClose();
|
||||
}
|
||||
|
||||
Exception WifiLanServerSocket::DoClose() {
|
||||
bool should_notify = !closed_;
|
||||
closed_ = true;
|
||||
if (should_notify) {
|
||||
cond_.SignalAll();
|
||||
if (close_notifier_) {
|
||||
auto notifier = std::move(close_notifier_);
|
||||
mutex_.Unlock();
|
||||
// Notifier may contain calls to public API, and may cause deadlock, if
|
||||
// mutex_ is held during the call.
|
||||
notifier();
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
/** WifiLanMedium implementation. */
|
||||
WifiLanMedium::~WifiLanMedium() {
|
||||
advertising_info_.Clear();
|
||||
discovering_info_.Clear();
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
|
||||
std::string service_type = nsd_service_info.GetServiceType();
|
||||
NSString* serviceType = ObjCStringFromCppString(service_type);
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (advertising_info_.Existed(service_type)) {
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start advertising because "
|
||||
@"service_type=%@, has started already",
|
||||
serviceType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve service name.
|
||||
NSString* serviceName = ObjCStringFromCppString(nsd_service_info.GetServiceName());
|
||||
// Retrieve TXTRecord and convert it to NSDictionary type.
|
||||
NSDictionary<NSString*, NSData*>* TXTRecordData =
|
||||
NSDictionaryFromCppTxtRecords(nsd_service_info.GetTxtRecords());
|
||||
// Get ip address and port to retrieve the server_socket, if not, then return nil.
|
||||
std::string ip_address = nsd_service_info.GetIPAddress();
|
||||
int port = nsd_service_info.GetPort();
|
||||
__block std::string socket_name = WifiLanServerSocket::GetName(ip_address, port);
|
||||
GNCMBonjourService* service = [[GNCMBonjourService alloc]
|
||||
initWithServiceName:serviceName
|
||||
serviceType:serviceType
|
||||
port:requesting_port_
|
||||
TXTRecordData:TXTRecordData
|
||||
endpointConnectedHandler:^GNCMConnectionHandlers*(id<GNCMConnection> connection) {
|
||||
auto item = server_sockets_.find(socket_name);
|
||||
WifiLanServerSocket* server_socket = item != server_sockets_.end() ? item->second : nullptr;
|
||||
if (!server_socket) {
|
||||
return nil;
|
||||
}
|
||||
auto socket = absl::make_unique<WifiLanSocket>(connection);
|
||||
GNCMConnectionHandlers* connectionHandlers =
|
||||
static_cast<WifiLanInputStream&>(socket->GetInputStream()).GetConnectionHandlers();
|
||||
server_socket->Connect(std::move(socket));
|
||||
return connectionHandlers;
|
||||
}];
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
advertising_info_.Add(service_type, service);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) {
|
||||
std::string service_type = nsd_service_info.GetServiceType();
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (!advertising_info_.Existed(service_type)) {
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan StopAdvertising: Can't stop advertising because we never "
|
||||
@"started advertising for service_type=%@",
|
||||
ObjCStringFromCppString(service_type));
|
||||
return false;
|
||||
}
|
||||
advertising_info_.Remove(service_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StartDiscovery(const std::string& service_type,
|
||||
DiscoveredServiceCallback callback) {
|
||||
NSString* serviceType = ObjCStringFromCppString(service_type);
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (discovering_info_.Existed(service_type)) {
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start discovery because "
|
||||
@"service_type=%@, has started already",
|
||||
serviceType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
GNCMBonjourBrowser* browser = [[GNCMBonjourBrowser alloc]
|
||||
initWithServiceType:serviceType
|
||||
endpointFoundHandler:^GNCMEndpointLostHandler(
|
||||
NSString* endpointId, NSString* serviceType, NSString* serviceName,
|
||||
NSDictionary<NSString*, NSData*>* _Nullable txtRecordData,
|
||||
GNCMConnectionRequester requestConnection) {
|
||||
__block NsdServiceInfo nsd_service_info = {};
|
||||
nsd_service_info.SetServiceName(CppStringFromObjCString(serviceName));
|
||||
nsd_service_info.SetServiceType(CppStringFromObjCString(serviceType));
|
||||
// Set TXTRecord converted from NSDictionary to hash map.
|
||||
if (txtRecordData != nil) {
|
||||
auto txt_records = AbslHashMapFromObjCTxtRecords(txtRecordData);
|
||||
nsd_service_info.SetTxtRecords(txt_records);
|
||||
}
|
||||
connection_requesters_.insert({CppStringFromObjCString(serviceType), requestConnection});
|
||||
callback.service_discovered_cb(nsd_service_info);
|
||||
return ^{
|
||||
callback.service_lost_cb(nsd_service_info);
|
||||
};
|
||||
}];
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
discovering_info_.Add(service_type, browser);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StopDiscovery(const std::string& service_type) {
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (!discovering_info_.Existed(service_type)) {
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan StopDiscovery: Can't stop discovering because "
|
||||
"we never started discovering.");
|
||||
return false;
|
||||
}
|
||||
discovering_info_.Remove(service_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
|
||||
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) {
|
||||
std::string service_type = remote_service_info.GetServiceType();
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan ConnectToService, service_type=%@",
|
||||
ObjCStringFromCppString(service_type));
|
||||
|
||||
GNCMConnectionRequester connection_requester = nil;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
const auto& it = connection_requesters_.find(service_type);
|
||||
if (it == connection_requesters_.end()) {
|
||||
return {};
|
||||
}
|
||||
connection_requester = it->second;
|
||||
}
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter(group);
|
||||
__block std::unique_ptr<WifiLanSocket> socket;
|
||||
if (connection_requester != nil) {
|
||||
if (cancellation_flag->Cancelled()) {
|
||||
GTMLoggerError(@"[NEARBY] WifiLan Connect: Has been cancelled: service_type=%@",
|
||||
ObjCStringFromCppString(service_type));
|
||||
dispatch_group_leave(group); // unblock
|
||||
return {};
|
||||
}
|
||||
|
||||
connection_requester(^(id<GNCMConnection> connection) {
|
||||
// If the connection wasn't successfully established, return a NULL socket.
|
||||
if (connection) {
|
||||
socket = absl::make_unique<WifiLanSocket>(connection);
|
||||
}
|
||||
|
||||
dispatch_group_leave(group); // unblock
|
||||
return socket != nullptr ? static_cast<WifiLanInputStream&>(socket->GetInputStream())
|
||||
.GetConnectionHandlers()
|
||||
: nullptr;
|
||||
});
|
||||
}
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
|
||||
return std::move(socket);
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
|
||||
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) {
|
||||
// Not implemented.
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(int port) {
|
||||
auto server_socket = std::make_unique<WifiLanServerSocket>();
|
||||
// The fake ip address and port need to be set here since they can't be retrieved before
|
||||
// StartAadvertising begins. Furthermore, NSNetService can't resolve ip address when finding
|
||||
// service. Try to fake them and make it socket name as a key of server_sockets_ which acts
|
||||
// the same socket binding.
|
||||
server_socket->SetIPAddress(GetFakeIPAddress());
|
||||
requesting_port_ = port;
|
||||
server_socket->SetPort(requesting_port_ == 0 ? GetFakePort() : requesting_port_);
|
||||
std::string socket_name =
|
||||
WifiLanServerSocket::GetName(server_socket->GetIPAddress(), server_socket->GetPort());
|
||||
server_socket->SetCloseNotifier([this, socket_name]() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
server_sockets_.erase(socket_name);
|
||||
});
|
||||
GTMLoggerInfo(@"[NEARBY] WifiLan Adding server socket, socket_name=%@",
|
||||
ObjCStringFromCppString(socket_name));
|
||||
absl::MutexLock lock(&mutex_);
|
||||
server_sockets_.insert({socket_name, server_socket.get()});
|
||||
return server_socket;
|
||||
}
|
||||
|
||||
std::string WifiLanMedium::GetFakeIPAddress() const {
|
||||
std::string ip_address;
|
||||
ip_address.resize(4);
|
||||
uint32_t raw_ip_addr = Prng().NextUint32();
|
||||
ip_address[0] = static_cast<char>(raw_ip_addr >> 24);
|
||||
ip_address[1] = static_cast<char>(raw_ip_addr >> 16);
|
||||
ip_address[2] = static_cast<char>(raw_ip_addr >> 8);
|
||||
ip_address[3] = static_cast<char>(raw_ip_addr >> 0);
|
||||
|
||||
return ip_address;
|
||||
}
|
||||
|
||||
int WifiLanMedium::GetFakePort() const {
|
||||
uint16_t port = Prng().NextUint32();
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,30 @@
|
||||
# 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 = "Shared",
|
||||
srcs = [
|
||||
"GNCUtils.m",
|
||||
],
|
||||
hdrs = [
|
||||
"GNCUtils.h",
|
||||
],
|
||||
deps = [
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_StringEncoding",
|
||||
"//smhasher:libmurmur3",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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
|
||||
|
||||
// Current version of socket protocol.
|
||||
extern const int GNCSocketVersion;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Generates a SHA-256 hash (32 bytes) from a data object.
|
||||
NSData *_Nullable GNCSha256Data(NSData *data);
|
||||
|
||||
/// Generates a SHA-256 hash (32 bytes) from a string.
|
||||
NSData *_Nullable GNCSha256String(NSString *string);
|
||||
|
||||
|
||||
/// Generates an MD5 hash (16 bytes) from a data object.
|
||||
NSData *_Nullable GNCMd5Data(NSData *data);
|
||||
|
||||
/// Generates an MD5 hash (16 bytes) from a string.
|
||||
NSData *_Nullable GNCMd5String(NSString *string);
|
||||
|
||||
|
||||
/// Base64-encode.
|
||||
NSString *GNCBase64Encode(NSData *data);
|
||||
|
||||
/// Base64-decode.
|
||||
NSData *GNCBase64Decode(NSString *string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,65 @@
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Shared/GNCUtils.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMStringEncoding.h"
|
||||
// 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.
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
const int GNCSocketVersion = 2;
|
||||
|
||||
NSData *GNCSha256Data(NSData *data) {
|
||||
unsigned char output[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_LONG length = (CC_LONG)data.length;
|
||||
if (!CC_SHA256(data.bytes, length, output)) return nil;
|
||||
return [NSData dataWithBytes:output length:CC_SHA256_DIGEST_LENGTH];
|
||||
}
|
||||
|
||||
NSData *GNCSha256String(NSString *string) {
|
||||
return GNCSha256Data([string dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
NSData *GNCMd5Data(NSData *data) {
|
||||
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(data.bytes, (CC_LONG)data.length, md5Buffer);
|
||||
return [NSData dataWithBytes:md5Buffer length:CC_MD5_DIGEST_LENGTH];
|
||||
}
|
||||
|
||||
NSData *GNCMd5String(NSString *string) {
|
||||
return GNCMd5Data([string dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
static GTMStringEncoding *GNCBase64WebSafeEncodingNoPadding() {
|
||||
static dispatch_once_t onceToken;
|
||||
static GTMStringEncoding *encoding;
|
||||
dispatch_once(&onceToken, ^{
|
||||
encoding = [GTMStringEncoding stringEncodingWithString:
|
||||
@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"];
|
||||
[encoding setDoPad:NO];
|
||||
});
|
||||
return encoding;
|
||||
}
|
||||
|
||||
NSString *GNCBase64Encode(NSData *data) {
|
||||
return [GNCBase64WebSafeEncodingNoPadding() encode:data error:nil];
|
||||
}
|
||||
|
||||
NSData *GNCBase64Decode(NSString *string) {
|
||||
return [GNCBase64WebSafeEncodingNoPadding() decode:string error:nil];
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,64 @@
|
||||
load("//tools/build_defs/apple:ios.bzl", "ios_unit_test")
|
||||
|
||||
# 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 = ["//visibility:public"])
|
||||
|
||||
objc_library(
|
||||
name = "PlatformTestslib",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"Platform/GNCCryptoTest.mm",
|
||||
"Platform/GNCInputFileTest.mm",
|
||||
"Platform/GNCMultiThreadExecutorTest.mm",
|
||||
"Platform/GNCScheduledExecutorTest.mm",
|
||||
"Platform/GNCSingleThreadExecutorTest.mm",
|
||||
],
|
||||
deps = [
|
||||
"//base",
|
||||
"//platform/impl/ios:Connections",
|
||||
],
|
||||
)
|
||||
|
||||
ios_unit_test(
|
||||
name = "PlatformTests",
|
||||
minimum_os_version = "12.0",
|
||||
runner = "//testing/utp/ios:IOS_12",
|
||||
deps = [
|
||||
":PlatformTestslib",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SharedTestslib",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"Shared/GNCUtilsTest.mm",
|
||||
],
|
||||
deps = [
|
||||
"//platform/impl/ios/Source/Platform",
|
||||
"//platform/impl/ios/Source/Shared",
|
||||
],
|
||||
)
|
||||
|
||||
ios_unit_test(
|
||||
name = "SharedTests",
|
||||
minimum_os_version = "12.0",
|
||||
runner = "//testing/utp/ios:IOS_12",
|
||||
deps = [
|
||||
":SharedTestslib",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#include "third_party/nearby_connections/cpp/platform/api/crypto.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
|
||||
@interface GNCCryptoTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCCryptoTest
|
||||
|
||||
// Tests that the hash functions return the expected values.
|
||||
- (void)testHashValues {
|
||||
std::string string("com.google.location.nearby");
|
||||
|
||||
// SHA-256 test.
|
||||
{
|
||||
const uint8_t sha256ExpectedHash[] = {
|
||||
0x09, 0x8e, 0x3c, 0x54, 0x2d, 0xee, 0x9e, 0x35,
|
||||
0x1d, 0xe7, 0x5b, 0xcf, 0xda, 0xb5, 0x62, 0xd3,
|
||||
0xde, 0xba, 0x14, 0x49, 0xbd, 0xf5, 0x95, 0x04,
|
||||
0x1f, 0x1d, 0x99, 0x84, 0x87, 0xc3, 0xcb, 0x8a, };
|
||||
location::nearby::ByteArray sha256Hash = location::nearby::Crypto::Sha256(string);
|
||||
XCTAssert(sha256Hash.size() == sizeof(sha256ExpectedHash) &&
|
||||
memcmp(sha256Hash.data(), sha256ExpectedHash, sizeof(sha256ExpectedHash)) == 0);
|
||||
}
|
||||
|
||||
// MD5 test.
|
||||
{
|
||||
const uint8_t md5ExpectedHash[] = {
|
||||
0x97, 0x78, 0x1d, 0x3d, 0xee, 0xd7, 0xdc, 0x5a,
|
||||
0x6e, 0xee, 0x50, 0x08, 0xce, 0xd1, 0xb2, 0xe8 };
|
||||
location::nearby::ByteArray md5Hash = location::nearby::Crypto::Md5(string);
|
||||
XCTAssert(md5Hash.size() == sizeof(md5ExpectedHash) &&
|
||||
memcmp(md5Hash.data(), md5ExpectedHash, sizeof(md5ExpectedHash)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#import "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/base/exception.h"
|
||||
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/input_file.h"
|
||||
|
||||
using ::location::nearby::ios::InputFile;
|
||||
using ::location::nearby::ByteArray;
|
||||
using ::location::nearby::ExceptionOr;
|
||||
|
||||
@interface GNCInputFileTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCInputFileTest
|
||||
|
||||
// TODO(b/169292092): Find more tools (e.g. file/util/TempPath) to test fake file.
|
||||
- (void)testNonExistentPath {
|
||||
std::string cc_path("/foo/bar/test.ext");
|
||||
NSString* objcPath = [NSString stringWithUTF8String:cc_path.c_str()];
|
||||
NSURL *testURL = [NSURL URLWithString:objcPath];
|
||||
|
||||
auto input_file = std::make_unique<InputFile>(testURL);
|
||||
XCTAssert(input_file != nullptr);
|
||||
|
||||
XCTAssertEqual(input_file->GetTotalSize(), 0);
|
||||
ExceptionOr<ByteArray> read_result = input_file->Read(3);
|
||||
XCTAssertFalse(read_result.ok());
|
||||
}
|
||||
|
||||
- (void)testGetFilePath {
|
||||
std::string cc_path("/foo/bar/test.ext");
|
||||
NSString* objcPath = [NSString stringWithUTF8String:cc_path.c_str()];
|
||||
NSURL *testURL = [NSURL URLWithString:objcPath];
|
||||
|
||||
auto input_file = std::make_unique<InputFile>(testURL);
|
||||
XCTAssert(input_file != nullptr);
|
||||
|
||||
std::string input_file_path = input_file->GetFilePath();
|
||||
XCTAssertTrue([objcPath isEqualToString:[NSString stringWithUTF8String:input_file_path.c_str()]]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#include "third_party/absl/time/time.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/cancelable.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/platform.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/submittable_executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
|
||||
using location::nearby::Runnable;
|
||||
using location::nearby::api::ImplementationPlatform;
|
||||
using MultiThreadExecutor = location::nearby::api::SubmittableExecutor;
|
||||
|
||||
@interface GNCMultiThreadExecutorTest : XCTestCase
|
||||
@property(atomic) int counter;
|
||||
@property(atomic) int otherCounter;
|
||||
@end
|
||||
|
||||
@implementation GNCMultiThreadExecutorTest
|
||||
|
||||
// Creates a MultiThreadExecutor.
|
||||
- (std::unique_ptr<MultiThreadExecutor>)executor {
|
||||
std::unique_ptr<MultiThreadExecutor> executor =
|
||||
ImplementationPlatform::CreateMultiThreadExecutor(4);
|
||||
XCTAssert(executor != nullptr);
|
||||
return executor;
|
||||
}
|
||||
|
||||
// Tests that the executor executes runnables as expected.
|
||||
- (void)testExecute {
|
||||
std::unique_ptr<MultiThreadExecutor> executor([self executor]);
|
||||
|
||||
// Execute two runnables that increment the counter.
|
||||
const int kIncrements = 13;
|
||||
Runnable incrementer = [self]() { self.counter++; };
|
||||
for (int i = 0; i < kIncrements; i++) {
|
||||
executor->Execute(std::move(incrementer));
|
||||
}
|
||||
|
||||
// Check that the counter has the expected value after giving the runnables time to run.
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
XCTAssertEqual(self.counter, kIncrements);
|
||||
}
|
||||
|
||||
// Tests that the executor submits runnables as expected.
|
||||
- (void)testSubmit {
|
||||
std::unique_ptr<MultiThreadExecutor> executor([self executor]);
|
||||
|
||||
// Submit two runnables that increment the counter.
|
||||
const int kIncrements = 13;
|
||||
Runnable incrementer = [self]() { self.counter++; };
|
||||
for (int i = 0; i < kIncrements; i++) {
|
||||
executor->DoSubmit(std::move(incrementer));
|
||||
}
|
||||
|
||||
// Check that the counter has the expected value after giving the runnables time to run.
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
XCTAssertEqual(self.counter, kIncrements);
|
||||
}
|
||||
|
||||
// Tests that fails to submit when the executor is shut down.
|
||||
- (void)testFailtoSubmitAfterShutdown {
|
||||
std::unique_ptr<MultiThreadExecutor> executor([self executor]);
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
XCTAssertFalse(executor->DoSubmit([self]() { self.counter++; }));
|
||||
|
||||
// Check that the counter has the expected value after giving the runnables time to run.
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
XCTAssertEqual(self.counter, 0);
|
||||
}
|
||||
|
||||
// Tests that when the executor is shut down, it waits for pending operations to finish, and no new
|
||||
// operations will be executed.
|
||||
- (void)testShutdownToAllowExistingTaskComplete {
|
||||
std::unique_ptr<MultiThreadExecutor> executor([self executor]);
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
const int kRunnableCount = 1000;
|
||||
for (int i = 0; i < kRunnableCount; i++) {
|
||||
executor->Execute([self]() { self.counter++; });
|
||||
}
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
executor->Execute([self]() { self.otherCounter++; });
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertLessThanOrEqual(self.counter, kRunnableCount);
|
||||
XCTAssertEqual(self.otherCounter, 0);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:0.5 handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#include "third_party/absl/time/time.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/cancelable.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/platform.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/scheduled_executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
|
||||
using location::nearby::Runnable;
|
||||
using location::nearby::api::ImplementationPlatform;
|
||||
using location::nearby::api::ScheduledExecutor;
|
||||
|
||||
@interface GNCScheduledExecutorTest : XCTestCase
|
||||
@property(atomic) int counter;
|
||||
@end
|
||||
|
||||
@implementation GNCScheduledExecutorTest
|
||||
|
||||
// Creates a ScheduledExecutor.
|
||||
- (std::unique_ptr<ScheduledExecutor>)executor {
|
||||
std::unique_ptr<ScheduledExecutor> executor = ImplementationPlatform::CreateScheduledExecutor();
|
||||
XCTAssert(executor != nullptr);
|
||||
return executor;
|
||||
}
|
||||
|
||||
// Tests that the executor schedules operation at the specified time.
|
||||
- (void)testScheduling {
|
||||
std::unique_ptr<ScheduledExecutor> executor([self executor]);
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
Runnable incrementer = [self]() { self.counter++; };
|
||||
|
||||
void (^checkCounter)(int, NSTimeInterval, dispatch_block_t) =
|
||||
^(int expectedCount, NSTimeInterval delay, dispatch_block_t finalBlock) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), ^{
|
||||
XCTAssertEqual(self.counter, expectedCount);
|
||||
finalBlock();
|
||||
});
|
||||
};
|
||||
|
||||
// Schedule two runnables that increment the counter, at 0.4 and 0.8 seconds.
|
||||
executor->Schedule(std::move(incrementer), absl::Seconds(0.4));
|
||||
executor->Schedule(std::move(incrementer), absl::Seconds(0.8));
|
||||
|
||||
// Check that the counter contains the expected values at 0.2, 0.6, and 1.0 seconds.
|
||||
checkCounter(0, 0.2, ^{});
|
||||
checkCounter(1, 0.6, ^{});
|
||||
checkCounter(2, 1.0, ^{ [expectation fulfill]; });
|
||||
|
||||
[self waitForExpectationsWithTimeout:1.2 handler:nil];
|
||||
}
|
||||
|
||||
// Tests that fails to schedule when the executor is shut down.
|
||||
- (void)testFailtoScheduleAfterShutdown {
|
||||
std::unique_ptr<ScheduledExecutor> executor([self executor]);
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
const NSTimeInterval delay = 0.1;
|
||||
executor->Schedule([self]() { self.counter++; }, absl::Milliseconds(delay));
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * 2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertEqual(self.counter, 0);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:delay * 5 handler:nil];
|
||||
}
|
||||
|
||||
// Tests that fails to cancel when the executor is shut down.
|
||||
- (void)testFailToCancelAfterShutdown {
|
||||
std::unique_ptr<ScheduledExecutor> executor([self executor]);
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
auto cancelable = executor->Schedule([self]() { self.counter++; }, absl::Seconds(0.1));
|
||||
|
||||
XCTAssert(cancelable.get() == nullptr);
|
||||
}
|
||||
|
||||
// Tests that shutting down an existing task fails to complete.
|
||||
- (void)testShutdownToFailExistingTask {
|
||||
std::unique_ptr<ScheduledExecutor> executor([self executor]);
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
const NSTimeInterval delay = 0.1;
|
||||
executor->Schedule([self]() { self.counter++; }, absl::Milliseconds(delay));
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * 2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertEqual(self.counter, 0);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:delay * 5 handler:nil];
|
||||
}
|
||||
|
||||
// Tests that a canceled runnable doesn't run.
|
||||
- (void)testCancelable {
|
||||
std::unique_ptr<ScheduledExecutor> executor([self executor]);
|
||||
|
||||
// This runnable should run but not increment the counter because it sleeps for longer than the
|
||||
// cancel below.
|
||||
const NSTimeInterval delay = 0.1;
|
||||
auto cancelable = executor->Schedule([self]() { self.counter++; }, absl::Seconds(delay));
|
||||
XCTAssert(cancelable.get() != nullptr);
|
||||
|
||||
// Cancel the runnable.
|
||||
cancelable->Cancel();
|
||||
|
||||
// Wait for the time interval to pass and verify that it didn't run.
|
||||
[NSThread sleepForTimeInterval:delay * 1.1];
|
||||
XCTAssertEqual(self.counter, 0);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,95 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#include "third_party/absl/time/time.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/cancelable.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/platform.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/api/submittable_executor.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
|
||||
|
||||
using location::nearby::Runnable;
|
||||
using location::nearby::api::ImplementationPlatform;
|
||||
using SingleThreadExecutor = location::nearby::api::SubmittableExecutor;
|
||||
|
||||
@interface GNCSingleThreadExecutorTest : XCTestCase
|
||||
@property(atomic) int counter;
|
||||
@end
|
||||
|
||||
@implementation GNCSingleThreadExecutorTest
|
||||
|
||||
// Creates a SingleThreadExecutor.
|
||||
- (std::unique_ptr<SingleThreadExecutor>)executor {
|
||||
std::unique_ptr<SingleThreadExecutor> executor =
|
||||
ImplementationPlatform::CreateSingleThreadExecutor();
|
||||
XCTAssert(executor != nullptr);
|
||||
return executor;
|
||||
}
|
||||
|
||||
// Tests that the executor executes runnables as expected.
|
||||
- (void)testRunnables {
|
||||
std::unique_ptr<SingleThreadExecutor> executor([self executor]);
|
||||
|
||||
Runnable incrementer = [self]() { self.counter++; };
|
||||
|
||||
// Schedule two runnables that increment the counter.
|
||||
executor->Execute(std::move(incrementer));
|
||||
executor->Execute(std::move(incrementer));
|
||||
|
||||
// Check that the counter has the expected value after a moment.
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
XCTAssertEqual(self.counter, 2);
|
||||
}
|
||||
|
||||
// Tests that fails to execute when the executor is shut down.
|
||||
- (void)testShutdownBeforeInvokeTask {
|
||||
std::unique_ptr<SingleThreadExecutor> executor([self executor]);
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
executor->Execute([self]() { self.counter++; });
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertEqual(self.counter, 0);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:0.5 handler:nil];
|
||||
}
|
||||
|
||||
// Tests that shutting down an existing task allows to complete.
|
||||
- (void)testShutdownToAllowExistingTaskComplete {
|
||||
std::unique_ptr<SingleThreadExecutor> executor([self executor]);
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
executor->Execute([self]() { self.counter++; });
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertEqual(self.counter, 1);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:0.5 handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 <XCTest/XCTest.h>
|
||||
|
||||
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
|
||||
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
|
||||
|
||||
using location::nearby::ByteArray;
|
||||
using location::nearby::ByteArrayFromNSData;
|
||||
using location::nearby::CppStringFromObjCString;
|
||||
using location::nearby::ObjCStringFromCppString;
|
||||
|
||||
@interface GNCUtilsTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCUtilsTest
|
||||
|
||||
// Tests that strings can be converted between C++ and Obj-C.
|
||||
- (void)testStrings {
|
||||
XCTAssert([ObjCStringFromCppString(std::string("Hey")) isEqual:@"Hey"]);
|
||||
XCTAssert(CppStringFromObjCString(@"Dude") == std::string("Dude"));
|
||||
}
|
||||
|
||||
// Tests that data objects can be converted between C++ and Obj-C.
|
||||
- (void)testDataObjects {
|
||||
uint8_t bytes[] = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef};
|
||||
ByteArray byteArray = ByteArray{(const char *)bytes, sizeof(bytes)};
|
||||
NSData *nsData = [NSData dataWithBytes:bytes length:sizeof(bytes)];
|
||||
|
||||
XCTAssert([NSDataFromByteArray(byteArray) isEqual:nsData]);
|
||||
XCTAssert(memcmp(ByteArrayFromNSData(nsData).data(), bytes, sizeof(bytes)) == 0);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -49,7 +49,6 @@ cc_library(
|
||||
hdrs = ["file.h"],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//platform/impl:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -52,10 +52,10 @@ cc_library(
|
||||
],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//analytics:__subpackages__",
|
||||
"//core:__subpackages__",
|
||||
"//platform/base:__pkg__",
|
||||
"//platform/impl/ios:__subpackages__",
|
||||
"//platform/impl/windows:__subpackages__",
|
||||
"//platform/public:__pkg__",
|
||||
],
|
||||
|
||||
@@ -26,7 +26,7 @@ proto_library(
|
||||
"//buildenv/target:non_prod",
|
||||
],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//platform/impl/ios:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user