summaryrefslogtreecommitdiff
path: root/chromium/third_party/nearby/src/internal/platform/implementation/ios
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/nearby/src/internal/platform/implementation/ios')
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/BUILD72
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.pngbin0 -> 78999 bytes
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj355
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h34
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m23
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json98
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json6
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard25
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard46
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist51
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h24
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m291
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m27
-rwxr-xr-xchromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/README.md198
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/XcodeSetup.pngbin0 -> 714590 bytes
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCAdvertiser.h84
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnection.h167
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnections.h20
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCDiscoverer.h95
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCPayload.h64
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCAdvertiser.mm317
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.h55
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.mm94
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h44
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.mm188
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCDiscoverer.mm401
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h36
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload.mm94
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h55
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.mm218
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.h42
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.mm77
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/platform.mm151
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/BUILD56
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.h18
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.m27
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h101
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.m31
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h42
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.m176
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h38
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.m224
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h48
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.m88
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h18
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.m17
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/BUILD94
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean.h46
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean_test.cc78
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32.h47
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32_test.cc64
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.cc37
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.h48
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable_test.cc84
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.cc40
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.h49
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch_test.cc83
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/crypto.mm39
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.h48
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.mm69
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.h47
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.mm87
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h47
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.mm40
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex.h84
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex_test.cc92
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.h68
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.mm144
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/single_thread_executor.h35
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/system_clock.cc33
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.h74
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.mm104
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.h202
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.mm497
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/BUILD30
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.h50
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.m65
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/BUILD63
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCCryptoTest.mm52
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCInputFileTest.mm56
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCMultiThreadExecutorTest.mm114
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCScheduledExecutorTest.mm139
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCSingleThreadExecutorTest.mm95
-rw-r--r--chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Shared/GNCUtilsTest.mm46
84 files changed, 7426 insertions, 0 deletions
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/BUILD b/chromium/third_party/nearby/src/internal/platform/implementation/ios/BUILD
new file mode 100644
index 00000000000..7c5c5be7ed7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/BUILD
@@ -0,0 +1,72 @@
+# 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.
+load("//tools/build_defs/apple:ios.bzl", "ios_static_framework")
+
+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.mm",
+ "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 = [
+ "//connections:core",
+ "//connections:core_types",
+ "//internal/platform/implementation/ios/Source/Mediums",
+ "//internal/platform/implementation/ios/Source/Platform",
+ "//internal/platform/implementation/ios/Source/Shared",
+ "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
+ "@com_google_absl//absl/functional:bind_front",
+ ],
+)
+
+MIN_IOS_VERSION = "12.0"
+
+HDRS_POD = [
+ "Source/GNCAdvertiser.h",
+ "Source/GNCConnection.h",
+ "Source/GNCDiscoverer.h",
+ "Source/GNCPayload.h",
+]
+
+ios_static_framework(
+ name = "NearbyConnections_framework",
+ hdrs = HDRS_POD,
+ bundle_name = "NearbyConnections",
+ minimum_os_version = MIN_IOS_VERSION,
+ deps = [
+ ":Connections",
+ ],
+)
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png
new file mode 100644
index 00000000000..c495d68b156
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png
Binary files differ
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj
new file mode 100644
index 00000000000..5f57eaffa2d
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj
@@ -0,0 +1,355 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C2208F900100EBA53E /* AppDelegate.m */; };
+ 41B7A2C6208F900100EBA53E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C5208F900100EBA53E /* ViewController.m */; };
+ 41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2C7208F900100EBA53E /* Main.storyboard */; };
+ 41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CA208F900200EBA53E /* Assets.xcassets */; };
+ 41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */; };
+ 41B7A2D1208F900200EBA53E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2D0208F900200EBA53E /* main.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearbyConnectionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 41B7A2C1208F900100EBA53E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 41B7A2C2208F900100EBA53E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 41B7A2C4208F900100EBA53E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+ 41B7A2C5208F900100EBA53E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+ 41B7A2C8208F900100EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 41B7A2CA208F900200EBA53E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 41B7A2CD208F900200EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 41B7A2CF208F900200EBA53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 41B7A2D0208F900200EBA53E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 41B7A2BB208F900100EBA53E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 41B7A2B5208F900000EBA53E = {
+ isa = PBXGroup;
+ children = (
+ 41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */,
+ 41B7A2BF208F900100EBA53E /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 41B7A2BF208F900100EBA53E /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */ = {
+ isa = PBXGroup;
+ children = (
+ 41B7A2C1208F900100EBA53E /* AppDelegate.h */,
+ 41B7A2C2208F900100EBA53E /* AppDelegate.m */,
+ 41B7A2C4208F900100EBA53E /* ViewController.h */,
+ 41B7A2C5208F900100EBA53E /* ViewController.m */,
+ 41B7A2C7208F900100EBA53E /* Main.storyboard */,
+ 41B7A2CA208F900200EBA53E /* Assets.xcassets */,
+ 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */,
+ 41B7A2CF208F900200EBA53E /* Info.plist */,
+ 41B7A2D0208F900200EBA53E /* main.m */,
+ );
+ path = NearbyConnectionsExample;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */;
+ buildPhases = (
+ 41B7A2BA208F900100EBA53E /* Sources */,
+ 41B7A2BB208F900100EBA53E /* Frameworks */,
+ 41B7A2BC208F900100EBA53E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = NearbyConnectionsExample;
+ productName = NearbyConnectionsExample;
+ productReference = 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 41B7A2B6208F900000EBA53E /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = Google;
+ TargetAttributes = {
+ 41B7A2BD208F900100EBA53E = {
+ CreatedOnToolsVersion = 9.3;
+ };
+ };
+ };
+ buildConfigurationList = 41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 41B7A2B5208F900000EBA53E;
+ productRefGroup = 41B7A2BF208F900100EBA53E /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 41B7A2BC208F900100EBA53E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */,
+ 41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */,
+ 41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 41B7A2BA208F900100EBA53E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 41B7A2C6208F900100EBA53E /* ViewController.m in Sources */,
+ 41B7A2D1208F900200EBA53E /* main.m in Sources */,
+ 41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 41B7A2C7208F900100EBA53E /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 41B7A2C8208F900100EBA53E /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 41B7A2CD208F900200EBA53E /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 41B7A2D2208F900200EBA53E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_BITCODE = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 41B7A2D3208F900200EBA53E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_BITCODE = NO;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 41B7A2D5208F900200EBA53E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE = "";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 41B7A2D6208F900200EBA53E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 41B7A2D2208F900200EBA53E /* Debug */,
+ 41B7A2D3208F900200EBA53E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 41B7A2D5208F900200EBA53E /* Debug */,
+ 41B7A2D6208F900200EBA53E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 41B7A2B6208F900000EBA53E /* Project object */;
+}
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h
new file mode 100644
index 00000000000..c1b1202cdf0
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2021 Google Inc.
+//
+// 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
+//
+// http://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.
+//
+// AppDelegate.h
+// NearbyConnectionsExample
+//
+
+#import <UIKit/UIKit.h>
+
+/**
+ * A delegate for NSApplication to handle notifications about app launch and
+ * shutdown. Owned by the application object.
+ */
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+/**
+ * Main screen window displayed to the user which contains any active view
+ * hierarchy.
+ */
+@property(nonatomic) UIWindow *window;
+
+@end
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m
new file mode 100644
index 00000000000..69022507528
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2021 Google Inc.
+//
+// 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
+//
+// http://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.
+//
+// AppDelegate.m
+// NearbyConnectionsExample
+//
+
+#import "AppDelegate.h"
+
+@implementation AppDelegate
+@end
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000..d8db8d65fd7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json
new file mode 100644
index 00000000000..da4a164c918
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000000..f83f6fd5810
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard
new file mode 100644
index 00000000000..b39e95c8009
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fos-Uz-R9B">
+ <device id="retina4_7" orientation="portrait">
+ <adaptation id="fullscreen"/>
+ </device>
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="BVh-fg-s4x">
+ <objects>
+ <viewController id="Tuj-3T-cB2" customClass="ViewController" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="9NC-2J-1xS">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+ <viewLayoutGuide key="safeArea" id="wwJ-gb-kZn"/>
+ </view>
+ <navigationItem key="navigationItem" id="ega-4i-ebx"/>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="xJQ-q8-wWg" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="433" y="-133"/>
+ </scene>
+ <!--Navigation Controller-->
+ <scene sceneID="Jcj-tp-czJ">
+ <objects>
+ <navigationController id="fos-Uz-R9B" sceneMemberID="viewController">
+ <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="x6x-US-muI">
+ <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </navigationBar>
+ <connections>
+ <segue destination="Tuj-3T-cB2" kind="relationship" relationship="rootViewController" id="Xa2-HD-mUn"/>
+ </connections>
+ </navigationController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="99I-fh-ObP" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="-455" y="-132"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist
new file mode 100644
index 00000000000..44aa9d70a41
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>NSLocalNetworkUsageDescription</key>
+ <string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
+ <key>NSBonjourServices</key>
+ <array>
+ <string>_54167B379724._tcp</string>
+ </array>
+</dict>
+</plist>
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h
new file mode 100644
index 00000000000..599609942ca
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2021 Google Inc.
+//
+// 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
+//
+// http://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.
+//
+// ViewController.h
+// NearbyConnectionsExample
+//
+
+#import <UIKit/UIKit.h>
+
+/** View controller for demo by loading NearbyConnections lib for advertiser and discoverer. */
+@interface ViewController : UIViewController
+@end
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m
new file mode 100644
index 00000000000..f6c8b82aa52
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m
@@ -0,0 +1,291 @@
+//
+// Copyright (c) 2021 Google Inc.
+//
+// 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
+//
+// http://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.
+//
+// ViewController.m
+// NearbyConnectionsExample
+//
+
+#import "ViewController.h"
+
+#import <NearbyConnections/NearbyConnections.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
+static NSString *kCellIdentifier = @"endpointCell";
+
+// Simplified version of dispatch_after.
+void delay(NSTimeInterval delay, dispatch_block_t block) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
+ dispatch_get_main_queue(), block);
+}
+
+// This class contains info about a discovered endpoint.
+@interface EndpointInfo : NSObject
+@property(nonatomic, readonly) id<GNCDiscoveredEndpointInfo> discInfo;
+@property(nonatomic, nullable) id<GNCConnection> connection;
+@end
+
+@implementation EndpointInfo
+- (instancetype)initWithDiscoveredInfo:(id<GNCDiscoveredEndpointInfo>)discInfo {
+ self = [super init];
+ if (self) {
+ _discInfo = discInfo;
+ }
+ return self;
+}
+@end
+
+@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
+@property(nonatomic) GNCAdvertiser *advertiser;
+@property(nonatomic) GNCDiscoverer *discoverer;
+@property(nonatomic) NSMutableDictionary<GNCEndpointId, EndpointInfo *> *endpoints;
+@property(nonatomic, readonly) NSData *ping;
+@property(nonatomic, readonly) NSData *pong;
+@property(nonatomic) UITableView *tableView;
+@property(nonatomic) UITextView *statusView;
+@property(nonatomic) NSMutableDictionary<GNCEndpointId, id<GNCConnection> > *incomingConnections;
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ [self makeViews];
+
+ // Enable "info" log messages in the release build.
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"GTMVerboseLogging"];
+
+ self.title = [[UIDevice currentDevice] name];
+ NSData *endpointInfo = [self.title dataUsingEncoding:NSUTF8StringEncoding];
+ _endpoints = [NSMutableDictionary dictionary];
+ _ping = [@"ping" dataUsingEncoding:NSUTF8StringEncoding];
+ _pong = [@"pong" dataUsingEncoding:NSUTF8StringEncoding];
+ _incomingConnections = [NSMutableDictionary dictionary];
+
+ // The advertiser.
+ _advertiser = [GNCAdvertiser
+ advertiserWithEndpointInfo:endpointInfo
+ serviceId:kServiceId
+ strategy:GNCStrategyCluster
+ connectionInitiationHandler:^(GNCEndpointId endpointId,
+ id<GNCAdvertiserConnectionInfo> advConnInfo,
+ GNCConnectionResponseHandler responseHandler) {
+ // Show a status that a discoverer has requested a connection.
+ [self logStatus:@"Accepting connection request" final:NO];
+
+ // Accept the connection request.
+ responseHandler(GNCConnectionResponseAccept);
+ return [GNCConnectionResultHandlers
+ successHandler:^(id<GNCConnection> connection) {
+ // Save the connection until the ping-pong sequence is done.
+ self.incomingConnections[endpointId] = connection;
+ __block BOOL receivedPong = NO;
+
+ // Send a ping, expecting the remote endpoint to send a pong.
+ [self logStatus:@"Connection established; sending ping" final:NO];
+ [connection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.ping]
+ completion:^(GNCPayloadResult result) {
+ if (result == GNCPayloadResultSuccess) {
+ [self logStatus:@"Sent ping; waiting for pong" final:NO];
+
+ // Show an error if the pong isn't received in the expected
+ // timeframe.
+ delay(3.0, ^{
+ if (receivedPong) {
+ [self logStatus:@"Error: Didn't receive pong" final:YES];
+ [self.incomingConnections
+ removeObjectForKey:endpointId]; // close the connection
+ }
+ });
+ } else {
+ [self logStatus:@"Error: Failed to send ping" final:YES];
+ }
+ }];
+
+ // Return handlers for incoming payloads.
+ return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
+ handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
+ receivedPong = NO;
+ [self.incomingConnections removeObjectForKey:endpointId]; // close the connection
+
+ // Show a status of whether the pong was received.
+ [self logStatus:[payload.bytes isEqual:self.pong] ? @"Received pong"
+ : @"Error: Payload is not pong"
+ final:YES];
+ };
+ }];
+ }
+ failureHandler:^(GNCConnectionFailure result) {
+ [self
+ logStatus:(result == GNCConnectionFailureRejected) ? @"Error: Connection rejected"
+ : @"Error: Connection failed"
+ final:YES];
+ }];
+ }];
+
+ // The discoverer.
+ __weak typeof(self) weakSelf = self;
+ _discoverer = [GNCDiscoverer
+ discovererWithServiceId:kServiceId
+ strategy:GNCStrategyCluster
+ endpointFoundHandler:^(GNCEndpointId endpointId,
+ id<GNCDiscoveredEndpointInfo> endpointInfo) {
+ typeof(self) self = weakSelf;
+
+ // An endpoint was discovered; add it to the endpoint list and UI.
+ self.endpoints[endpointId] = [[EndpointInfo alloc] initWithDiscoveredInfo:endpointInfo];
+ [self.tableView reloadData];
+
+ // Return the lost handler for this endpoint.
+ return ^{
+ typeof(self) self = weakSelf; // shadow
+
+ // Endpoint disappeared; remove it from the endpoint list and UI.
+ [self.endpoints removeObjectForKey:endpointId];
+ [self.tableView reloadData];
+ };
+ }];
+}
+
+#pragma mark - UITableViewDelegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ // The user tapped on a cell; request a connection with it.
+ EndpointInfo *info = _endpoints[_endpoints.allKeys[indexPath.row]];
+ if (!info) return;
+
+ void (^deselectRow)(void) = ^{
+ [tableView deselectRowAtIndexPath:indexPath animated:YES];
+ };
+
+ [self logStatus:@"Requesting connection" final:NO];
+ info.discInfo.requestConnection(
+ self.title,
+ ^(id<GNCDiscovererConnectionInfo> discConnInfo,
+ GNCConnectionResponseHandler responseHandler) {
+ // Accept the auth token.
+ [self logStatus:@"Accepting auth token" final:NO];
+ responseHandler(GNCConnectionResponseAccept);
+ return ^(id<GNCConnection> connection) {
+ // Save the connection until the ping-pong sequence is done.
+ info.connection = connection;
+ __block BOOL receivedPing = NO;
+
+ [self logStatus:@"Connection established; waiting for ping" final:NO];
+
+ // Show an error if the ping isn't received in the expected timeframe.
+ delay(3.0, ^{
+ if (!receivedPing) {
+ deselectRow();
+ [self logStatus:@"Error: Didn't receive ping" final:YES];
+ info.connection = nil; // close the connection
+ }
+ });
+
+ // Return handlers for incoming payloads.
+ __weak id<GNCConnection> weakConnection = connection; // avoid a retain cycle
+ return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
+ handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
+ receivedPing = YES;
+
+ // If a ping was received, send a pong back to the advertiser.
+ if ([payload.bytes isEqual:self.ping]) {
+ [self logStatus:@"Received ping; sending pong" final:NO];
+ [weakConnection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.pong]
+ completion:^(GNCPayloadResult result) {
+ deselectRow();
+ [self logStatus:(result == GNCPayloadResultSuccess)
+ ? @"Sent pong"
+ : @"Error: Failed to send pong"
+ final:YES];
+
+ // Pong was sent, so close the connection.
+ info.connection = nil;
+ }];
+ } else {
+ deselectRow();
+ [self logStatus:@"Error: Payload is not ping" final:YES];
+ }
+ };
+ }];
+ };
+ },
+ ^(GNCConnectionFailure result) {
+ // Connection failed.
+ deselectRow();
+ [self logStatus:(result == GNCConnectionFailureRejected) ? @"Connection rejected"
+ : @"Connection failed"
+ final:YES];
+ });
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView
+ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier
+ forIndexPath:indexPath];
+ cell.textLabel.text = _endpoints[_endpoints.allKeys[indexPath.row]].discInfo.name;
+ return cell;
+}
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return [_endpoints.allKeys count];
+}
+
+#pragma mark - Private
+
+- (void)makeViews {
+ _tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
+ [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier];
+ _tableView.delegate = self;
+ _tableView.dataSource = self;
+ _tableView.rowHeight = 48;
+ _tableView.scrollEnabled = YES;
+ _tableView.showsVerticalScrollIndicator = YES;
+ _tableView.userInteractionEnabled = YES;
+ _tableView.bounces = YES;
+ _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [self.view addSubview:_tableView];
+
+ // Make the status view.
+ UITextView * (^newTextView)(CGRect) = ^(CGRect frame) {
+ UITextView *textView = [[UITextView alloc] initWithFrame:frame];
+ textView.autoresizingMask =
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
+ textView.layer.borderColor = [UIColor blackColor].CGColor;
+ textView.layer.borderWidth = 1;
+ textView.editable = NO;
+ textView.textContainerInset = UIEdgeInsetsZero;
+ return textView;
+ };
+ CGRect selfFrame = self.view.frame;
+ static const int kStatusHeight = 144;
+ CGRect statusFrame = (CGRect){{selfFrame.origin.x + 4, selfFrame.size.height - kStatusHeight},
+ {selfFrame.size.width - 8, kStatusHeight - 4}};
+ _statusView = newTextView(statusFrame);
+ [self.view addSubview:_statusView];
+}
+
+- (void)logStatus:(NSString *)status final:(BOOL)final {
+ _statusView.text = [NSString stringWithFormat:@"%@\n%@%@", _statusView.text, status,
+ final ? @"\n–––––––––––––––––––––––––" : @""];
+ [_statusView scrollRangeToVisible:NSMakeRange(_statusView.text.length - 1, 1)];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m
new file mode 100644
index 00000000000..3111188ca69
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m
@@ -0,0 +1,27 @@
+//
+// Copyright (c) 2021 Google Inc.
+//
+// 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
+//
+// http://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.
+//
+// main.m
+// NearbyConnectionsExample
+//
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/README.md b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/README.md
new file mode 100755
index 00000000000..bf98df6070b
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/README.md
@@ -0,0 +1,198 @@
+# Nearby Connections Sample App for iOS
+
+This is a sample app for third party developers using the Nearby Connections
+library. On startup, it advertises and discovers. Discovered advertisers are
+added to the list in the UI. When the user taps on an advertiser in the list,
+the discoverer requests a connection with it. When the connection is
+established, the advertiser sends a "ping" payload to the discoverer, which
+sends "pong" payload back to the advertiser. The connection is then closed.
+
+## Setup
+
+1.Get the NearbyConnections_framework.zip from https://github.com/google/nearby/releases/tag/v0.0.1-ios, unzip it,
+and put your unzipped folder under your project folder. The directory structure
+looks like:
+
+```
+/NearbyConnectionsExample
+ /NearbyConnectionsExample
+ NearbyConnectionsExample.xcodeproj
+ README.md
+ /NearbyConnections.framework
+```
+
+2.Import NearbyConnections.framework
+
+- In Xcode, click the NearbyConnectionsExample in the left pane. And click the one in TARGETS-NearbyConnectionsExample at the left of right pane and the Build Phases at the right.
+- See the **Link Binary With Libraries**, and press **+** to import the file - **libc++.tbd**.
+- In Add **Other…**, import the NearbyConnections.framework folder which was unzipped.
+
+![Import framework in Xcode](./XcodeSetup.png)
+
+3.Update info.plist:
+
+- add **NSLocalNetworkUsageDescription** key with a description of your usage of Nearby Connections.
+- add **NSBonjourServices** key.
+ for NSBonjourServices key, add the bonjour type name: `_54167B379724._tcp`
+
+> **54167B379724** is the 6 byte hash of service id **com.google.NearbyConnectionsExample**
+
+```
+// NearbyConnectionsExample/info.plist
+...
+<key>NSLocalNetworkUsageDescription</key>
+<string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
+<key>NSBonjourServices</key>
+<array>
+ <string>_54167B379724._tcp</string>
+</array>
+...
+```
+
+
+4.Add service id and import `<NearbyConnections/NearbyConnections.h>` in your main view controller.
+
+```
+// NearbyConnectionsExample/ViewController.m
+
+static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
+
+#import <NearbyConnections/NearbyConnections.h>
+```
+
+## Code Snippets
+Note: All of the callbacks in this library use blocks rather than delegates. Be careful to avoid retain cycles in your block implementations. See the [Apple documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1) describing how to avoid retain cycles.
+
+Here is the skeleton code for an advertiser:
+
+```objc
+_advertiser = [GNCAdvertiser
+ advertiserWithEndpointInfo:endpointInfo
+ serviceId:myServiceId
+ strategy:GNCStrategyCluster
+ connectionInitiationHandler:^(GNCEndpointId endpointId,
+ id<GNCAdvertiserConnectionInfo> connectionInfo,
+ GNCConnectionResponseHandler responseHandler) {
+ // Decide whether to accept or reject the connection. The following code would normally
+ // exist in the callback for an alert, for instance.
+ if (/* user rejected */) {
+ responseHandler(GNCConnectionResponseReject); // the user rejected the invitation
+ } else {
+ responseHandler(GNCConnectionResponseAccept); // the user accepted the invitation
+
+ // Return connection result handlers, one of which is called depending on
+ // whether a successful connection was made.
+ return [GNCConnectionResultHandlers successHandler:^(id<GNCConnection> connection) {
+ // A successful connection was made. Save the connection somewhere, which can
+ // be used to send payloads to the remote endpoint.
+
+ // Return the incoming payload and disconnect handlers.
+ return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
+ // Optionally set the Bytes payload handler.
+ handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
+ // Process the payload received from the remote endpoint.
+ };
+
+ // Optionally set the Stream payload handler.
+ handlers.streamPayloadHandler = ^(GNCStreamPayload *payload, NSProgress *progress) {
+ // Receipt of a Stream payload has started. Input can be read from the payload’s
+ // NSInputStream, and progress/cancellation is handled via the NSProgress object.
+ return ^(GNCPayloadResult result) {
+ if (result == GNCPayloadResultSuccess) {
+ // The payload has been successfully received.
+ }
+ };
+ };
+
+ // Optionally set the disconnected handler.
+ handlers.disconnectedHandler = ^(GNCDisconnectedReason reason) {
+ // The connection was severed by either endpoint or lost.
+ };
+ }
+ failureHandler:^(GNCConnectionFailure result) {
+ // Failed to make the connection.
+ }]);
+ }
+}];
+```
+
+Here is the skeleton code for a discoverer:
+
+```objc
+_discoverer =
+ [GNCDiscoverer discovererWithServiceId:myServiceId
+ strategy:GNCStrategyCluster
+ endpointFoundHandler:^(GNCEndpointId endpointId,
+ id<GNCDiscoveredEndpointInfo> discEndpointInfo) {
+ // An endpoint was found. Typically you would add it to a list of nearby endpoints
+ // displayed in a UITableView, for instance.
+
+ // The following code shows how to request a connection with the endpoint. This code
+ // would normally exist in the -didSelectRowAtIndexPath: of UITableViewDelegate.
+ if (/* user wants to request a connection */) {
+ requestHandler(myName,
+ // This block is called once an authentication string is generated between the endpoints.
+ ^(id<GNCDiscovererConnectionInfo> discConnInfo, GNCConnectionResponseHandler responseHandler) {
+ // Ask the user to confirm the authentication string.
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Accept auth?" ...];
+ [alert addAction:[UIAlertAction actionWithTitle:@"OK"
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *action) {
+ responseHandler(GNCConnectionResponseAccept);
+ }]];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *action) {
+ responseHandler(GNCConnectionResponseReject);
+ }]];
+ [self presentViewController:alert animated:YES completion:^{}];
+
+ // Return a block that's called if the connection was successful.
+ return ^(id<GNCConnection> connection) {
+ // A successful connection was made. Save the connection somewhere, which can
+ // be used to send payloads to the remote endpoint.
+
+ // Return incoming data handlers as in the advertiser example above.
+ return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
+ // Set up payload and disconnect handlers here as in the advertiser example above.
+ }];
+ };
+ },
+ // This block is called if the connection failed for any reason.
+ ^(GNCConnectionFailure result) {
+ // Typically an alert would be shown here explaining why the connection failed.
+ });
+ }
+
+ // Return the endpoint-lost handler, which is called when the endpoint goes out of range
+ // or stops advertising.
+ return ^{
+ // The endpoint disappeared.
+ };
+}];
+```
+
+Here is an example of how to send a Bytes payload. The returned NSProgress object can be passed to UIProgressView to display a progress bar.
+
+```objc
+NSProgress *progress = [connection
+ sendBytesPayload:[GNCBytesPayload payloadWithBytes:someData]
+ completion:^(GNCPayloadResult result) {
+ // Check status to see if it was successfully sent.
+ }];
+```
+
+## Build and run
+
+![Sucessful running screenshot](./NearbyConnectionsExample.png)
+
+If you meet the following error in the debug panel of Xcode, you likely need to set up the keys listed in step 3 **Update info.plist**, as well as the service type.
+
+```
+1970-01-01 00:00:00.000 NearbyConnectionsExample[1383/0x16d87b000] [lvl=1] -[GNCMBonjourService netService:didNotPublish:] Error publishing: service: <NSNetService 0x283a18920> local _307BEAB11028._tcp. IjFQWEUwe-oAAA 51898, errorDic: {
+ NSNetServicesErrorCode = "-72008";
+ NSNetServicesErrorDomain = 10;
+}
+```
+---
+NOTE: The iOS simulator is unstable when advertising. We recommend using an real iOS device.
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/XcodeSetup.png b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/XcodeSetup.png
new file mode 100644
index 00000000000..4f353ffeaec
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Example/NearbyConnectionsExample/XcodeSetup.png
Binary files differ
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCAdvertiser.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCAdvertiser.h
new file mode 100644
index 00000000000..03272bb646d
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCAdvertiser.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.
+
+#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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnection.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnection.h
new file mode 100644
index 00000000000..85efca47ce5
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnection.h
@@ -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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnections.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnections.h
new file mode 100644
index 00000000000..71c280b012c
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCConnections.h
@@ -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"
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCDiscoverer.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCDiscoverer.h
new file mode 100644
index 00000000000..f9afe835a3f
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCDiscoverer.h
@@ -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 <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 endpointInfo A data for endpoint info which contains readable name of this endpoint,
+ * to be displayed on other endpoints.
+ * @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)(
+ NSData *endpointInfo, GNCDiscovererConnectionInitializationHandler connectionAuthorizationHandler,
+ GNCConnectionFailureHandler failureHandler);
+
+/** Information about an endpoint when it's discovered. */
+@protocol GNCDiscoveredEndpointInfo <NSObject>
+/** The human readable name of the remote endpoint. */
+@property(nonatomic, readonly, copy) NSString *endpointName;
+/** Information advertised by the remote endpoint. */
+@property(nonatomic, readonly, copy) NSData *endpointInfo;
+/** 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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCPayload.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCPayload.h
new file mode 100644
index 00000000000..83eef679275
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/GNCPayload.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.
+
+#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;
+
++ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCAdvertiser.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCAdvertiser.mm
new file mode 100644
index 00000000000..b00bbebdbc3
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCAdvertiser.mm
@@ -0,0 +1,317 @@
+// Copyright 2021 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 "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
+
+#include <string>
+
+#include "absl/functional/bind_front.h"
+#include "connections/advertising_options.h"
+#include "connections/core.h"
+#include "connections/listeners.h"
+#include "connections/params.h"
+#include "connections/status.h"
+#include "internal/platform/byte_array.h"
+#import "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
+#import "internal/platform/implementation/ios/Source/Platform/utils.h"
+#import "GoogleToolboxForMac/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::AdvertisingOptions;
+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),
+ AdvertisingOptions{
+ {
+ GNCStrategyToStrategy(strategy), // .strategy
+ location::nearby::connections::BooleanMediumSelector(), // .allowed
+ },
+ true, // .auto_upgrade_bandwidth
+ true, // .enforce_topology_constraints
+ },
+ 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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.h
new file mode 100644
index 00000000000..4c4f0e9e8b5
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.h
@@ -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 "connections/core.h"
+#include "connections/implementation/service_controller_router.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.mm
new file mode 100644
index 00000000000..0abfc851e4a
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCore.mm
@@ -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 "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+
+#include <utility>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/internal/common.h"
+#include "connections/core.h"
+#include "connections/implementation/service_controller_router.h"
+#include "internal/platform/payload_id.h"
+#import "GoogleToolboxForMac/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;
+}
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h
new file mode 100644
index 00000000000..08afddfd3ee
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h
@@ -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 "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.mm
new file mode 100644
index 00000000000..a389763e679
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.mm
@@ -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 "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
+
+#include "connections/core.h"
+#include "connections/payload.h"
+#include "internal/platform/exception.h"
+#include "internal/platform/file.h"
+#include "internal/platform/implementation/input_file.h"
+#import "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/ios/Source/GNCPayload.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+#import "internal/platform/implementation/ios/Source/Platform/utils.h"
+#include "internal/platform/input_stream.h"
+#include "internal/platform/payload_id.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.identifier;
+ 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.identifier;
+ // 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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCDiscoverer.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCDiscoverer.mm
new file mode 100644
index 00000000000..1175204bc6b
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCDiscoverer.mm
@@ -0,0 +1,401 @@
+// Copyright 2021 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 "internal/platform/implementation/ios/Source/GNCDiscoverer.h"
+
+#include <string>
+#include <utility>
+
+#include "absl/functional/bind_front.h"
+#include "connections/connection_options.h"
+#include "connections/core.h"
+#include "connections/discovery_options.h"
+#include "connections/listeners.h"
+#include "connections/status.h"
+#include "internal/platform/byte_array.h"
+#import "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
+#import "internal/platform/implementation/ios/Source/Platform/utils.h"
+#import "GoogleToolboxForMac/GTMLogger.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+using ::location::nearby::ByteArray;
+using ::location::nearby::CppStringFromObjCString;
+using ::location::nearby::connections::DiscoveryOptions;
+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 *endpointName;
+@property(nonatomic, copy) NSData *endpointInfo;
+@end
+
+@implementation GNCDiscoveredEndpointInfo
+
+@synthesize requestConnection = _requestConnection;
+
++ (instancetype)infoWithName:(NSString *)endpointName
+ endpointInfo:(NSData *)endpointInfo
+ requestConnection:(GNCConnectionRequester)requestConnection {
+ GNCDiscoveredEndpointInfo *info = [[GNCDiscoveredEndpointInfo alloc] init];
+ info.endpointName = endpointName;
+ info.endpointInfo = endpointInfo;
+ 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];
+
+ NSString *name = ObjCStringFromCppString(std::string(endpoint_info));
+ NSData *info = NSDataFromByteArray(endpoint_info);
+ GNCCore *core = discoverer.core; // don't capture |this| or |discoverer|
+ NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints = discoverer.endpoints;
+ GNCDiscoveredEndpointInfo *discEndpointInfo = [GNCDiscoveredEndpointInfo
+ infoWithName:name
+ endpointInfo:info
+ requestConnection:^(NSData *info,
+ 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 = ByteArrayFromNSData(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),
+ DiscoveryOptions{
+ {
+ GNCStrategyToStrategy(strategy),
+ },
+ },
+ std::move(listener), ResultListener{});
+
+ return discoverer;
+}
+
+- (void)dealloc {
+ GTMLoggerInfo(@"GNCDiscoverer deallocated");
+ _core->_core->StopDiscovery(ResultListener{});
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h
new file mode 100644
index 00000000000..4792826c7e9
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload.mm
new file mode 100644
index 00000000000..30c88fe5e7a
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayload.mm
@@ -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 "internal/platform/implementation/ios/Source/GNCPayload.h"
+
+#include "connections/payload.h"
+#include "internal/platform/payload_id.h"
+
+#include <stdlib.h>
+
+using ::location::nearby::connections::Payload;
+using ::location::nearby::PayloadId;
+
+NS_ASSUME_NONNULL_BEGIN
+
+PayloadId GenerateId() {
+ return Payload::GenerateId();
+}
+
+@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:GenerateId()];
+}
+
++ (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:GenerateId()];
+}
+
++ (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:GenerateId()];
+}
+
++ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
+ return [[self alloc] initWithFileURL:fileURL identifier:identifier];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h
new file mode 100644
index 00000000000..51ae1a1c998
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h
@@ -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 "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.mm
new file mode 100644
index 00000000000..29c0feec54a
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.mm
@@ -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 "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
+
+#include <string>
+
+#include "connections/core.h"
+#include "connections/listeners.h"
+#include "connections/payload.h"
+#include "internal/platform/byte_array.h"
+#include "internal/platform/exception.h"
+#include "internal/platform/file.h"
+#import "internal/platform/implementation/ios/Source/GNCConnection.h"
+#import "internal/platform/implementation/ios/Source/GNCPayload.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
+#import "internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h"
+#include "internal/platform/implementation/ios/Source/Platform/utils.h"
+#include "internal/platform/input_stream.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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.h
new file mode 100644
index 00000000000..7f1e7b66289
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.h
@@ -0,0 +1,42 @@
+// Copyright 2021 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 "connections/listeners.h"
+#import "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.mm
new file mode 100644
index 00000000000..2f3aa81db29
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/GNCUtils.mm
@@ -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 "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
+
+#include "connections/strategy.h"
+#import "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/platform.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/platform.mm
new file mode 100644
index 00000000000..a7366bb97b4
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Internal/platform.mm
@@ -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 "internal/platform/implementation/platform.h"
+
+#include <string>
+
+#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
+#include "internal/platform/implementation/ios/Source/Platform/atomic_boolean.h"
+#include "internal/platform/implementation/ios/Source/Platform/atomic_uint32.h"
+#include "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
+#include "internal/platform/implementation/ios/Source/Platform/count_down_latch.h"
+#include "internal/platform/implementation/ios/Source/Platform/input_file.h"
+#import "internal/platform/implementation/ios/Source/Platform/log_message.h"
+#import "internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h"
+#include "internal/platform/implementation/ios/Source/Platform/mutex.h"
+#import "internal/platform/implementation/ios/Source/Platform/scheduled_executor.h"
+#import "internal/platform/implementation/ios/Source/Platform/single_thread_executor.h"
+#import "internal/platform/implementation/ios/Source/Platform/utils.h"
+#include "internal/platform/implementation/ios/Source/Platform/wifi_lan.h"
+#include "internal/platform/implementation/mutex.h"
+#include "internal/platform/implementation/shared/file.h"
+#include "internal/platform/payload_id.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 shared::IOFile::CreateInputFile(GetPayloadPath(payload_id), total_size);
+ }
+}
+
+std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(PayloadId payload_id) {
+ return shared::IOFile::CreateOutputFile(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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/BUILD b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/BUILD
new file mode 100644
index 00000000000..65c811784c7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/BUILD
@@ -0,0 +1,56 @@
+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 = ["//internal/platform/implementation/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",
+ "//internal/platform/implementation/ios/Source/Shared",
+ "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
+ "@com_google_absl//absl/numeric:int128",
+ ],
+)
+
+objc_proto_library(
+ name = "ObjCProtos",
+ deps = [":Protos"],
+)
+
+proto_library(
+ name = "Protos",
+ deps = [
+ "//connections/implementation/proto:offline_wire_formats_proto",
+ ],
+)
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.h
new file mode 100644
index 00000000000..1b044fd3bd4
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.h
@@ -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);
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.m
new file mode 100644
index 00000000000..220326a9648
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCLeaks.m
@@ -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 "internal/platform/implementation/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
+}
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h
new file mode 100644
index 00000000000..67d32fb1dd1
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h
@@ -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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.m
new file mode 100644
index 00000000000..27b4c20fa62
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/GNCMConnection.m
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h
new file mode 100644
index 00000000000..6f5d338ee8f
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.m
new file mode 100644
index 00000000000..d88f8a10929
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.m
@@ -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 "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h"
+
+#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
+#import "GoogleToolboxForMac/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h
new file mode 100644
index 00000000000..e88ac9ee4ba
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.m
new file mode 100644
index 00000000000..ae318843348
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.m
@@ -0,0 +1,224 @@
+// 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 "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
+
+#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
+#import "GoogleToolboxForMac/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;
+
+@property(nonatomic) BOOL inputStreamOpen;
+@property(nonatomic) BOOL outputStreamOpen;
+@end
+
+@implementation GNCMBonjourConnection
+
+- (instancetype)initWithInputStream:(NSInputStream *)inputStream
+ outputStream:(NSOutputStream *)outputStream
+ queue:(nullable dispatch_queue_t)queue {
+ self = [super init];
+ if (self) {
+ _inputStreamOpen = NO;
+ _outputStreamOpen = NO;
+
+ _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:
+ if (stream == _inputStream) {
+ _inputStreamOpen = YES;
+ }
+ if (stream == _outputStream) {
+ _outputStreamOpen = YES;
+ }
+ // 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 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 (_inputStreamOpen && _outputStreamOpen && _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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h
new file mode 100644
index 00000000000..bdb31d26c86
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h
@@ -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.
+
+#import <Foundation/Foundation.h>
+
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.m
new file mode 100644
index 00000000000..aa2176fd221
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.m
@@ -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 "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
+
+#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
+#import "GoogleToolboxForMac/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h
new file mode 100644
index 00000000000..e314dec6295
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h
@@ -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;
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.m
new file mode 100644
index 00000000000..6f51015594d
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.m
@@ -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 "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
+
+NSString *const GNCMBonjourDomain = @"local";
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/BUILD b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/BUILD
new file mode 100644
index 00000000000..b739c1708d4
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/BUILD
@@ -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.
+licenses(["notice"])
+
+package(default_visibility = ["//internal/platform/implementation/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",
+ "//internal/platform/implementation:platform",
+ "//internal/platform/implementation:types",
+ "//internal/platform/implementation/ios/Source/Mediums",
+ "//internal/platform/implementation/ios/Source/Shared",
+ "//internal/platform/implementation/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 = [
+ "//internal/platform/implementation:platform",
+ "//internal/platform/implementation:types",
+ "@com_google_absl//absl/strings:str_format",
+ "@com_google_absl//absl/synchronization",
+ "@com_google_absl//absl/time",
+ ],
+)
+
+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",
+ "@com_github_protobuf_matchers//protobuf-matchers",
+ "@com_google_absl//absl/synchronization",
+ "@com_google_absl//absl/time",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_nisaba//nisaba/port:thread_pool/fiber",
+ ],
+)
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean.h
new file mode 100644
index 00000000000..ed3bbeebbf2
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean.h
@@ -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 "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean_test.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean_test.cc
new file mode 100644
index 00000000000..ed4c611db9d
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_boolean_test.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32.h
new file mode 100644
index 00000000000..d9eb3acd487
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32.h
@@ -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 "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32_test.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32_test.cc
new file mode 100644
index 00000000000..7893f3c2c1c
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/atomic_uint32_test.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.cc
new file mode 100644
index 00000000000..8ad8752e0ac
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.cc
@@ -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 "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
+
+#include "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.h
new file mode 100644
index 00000000000..65486ff59b0
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable.h
@@ -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 "internal/platform/implementation/condition_variable.h"
+#include "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable_test.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable_test.cc
new file mode 100644
index 00000000000..7d36f8641d7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/condition_variable_test.cc
@@ -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 "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
+
+#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.cc
new file mode 100644
index 00000000000..9fce8728ba3
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.h
new file mode 100644
index 00000000000..e5fcd6be20b
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch.h
@@ -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 "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch_test.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch_test.cc
new file mode 100644
index 00000000000..2122bdedeb6
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/count_down_latch_test.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/crypto.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/crypto.mm
new file mode 100644
index 00000000000..004cd56f561
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/crypto.mm
@@ -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 "internal/platform/implementation/crypto.h"
+
+#import "absl/strings/string_view.h"
+#import "internal/platform/implementation/ios/Source/Platform/utils.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.h
new file mode 100644
index 00000000000..6ccd59f78ef
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.h
@@ -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 "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.mm
new file mode 100644
index 00000000000..532f7e3461f
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/input_file.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/input_file.h"
+
+#include <string>
+
+#import "internal/platform/exception.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.h
new file mode 100644
index 00000000000..03e3dc1c593
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.h
@@ -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 "glog/logging.h"
+#include "internal/platform/implementation/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:
+ google::LogMessage log_streamer_;
+ api::LogMessage::Severity severity_;
+};
+
+} // namespace ios
+} // namespace nearby
+} // namespace location
+
+#endif // IPHONE_SHARED_NEARBY_CONNECTIONS_SOURCE_PLATFORM_LOG_MESSAGE_H_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.mm
new file mode 100644
index 00000000000..c3772da2191
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/log_message.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/log_message.h"
+
+#include "glog/logging.h"
+#include "internal/platform/implementation/log_message.h"
+#include "GoogleToolboxForMac/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h
new file mode 100644
index 00000000000..1a371580034
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h
@@ -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 "internal/platform/implementation/submittable_executor.h"
+#import "internal/platform/runnable.h"
+#import "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.mm
new file mode 100644
index 00000000000..e4a38712232
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/multi_thread_executor.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h"
+
+#import "internal/platform/implementation/ios/Source/Platform/scheduled_executor.h"
+#include "internal/platform/runnable.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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex.h
new file mode 100644
index 00000000000..b60484d4cd0
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex.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.
+
+#ifndef PLATFORM_IMPL_IOS_MUTEX_H_
+#define PLATFORM_IMPL_IOS_MUTEX_H_
+
+#include "absl/synchronization/mutex.h"
+#include "internal/platform/implementation/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex_test.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex_test.cc
new file mode 100644
index 00000000000..5c5a9dbe380
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/mutex_test.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.h
new file mode 100644
index 00000000000..580389997c4
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.h
@@ -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 "internal/platform/implementation/scheduled_executor.h"
+#include "internal/platform/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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.mm
new file mode 100644
index 00000000000..0df19ced163
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/scheduled_executor.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/scheduled_executor.h"
+
+#import <Foundation/Foundation.h>
+
+#include "absl/time/time.h"
+#import "internal/platform/implementation/ios/Source/Platform/atomic_boolean.h"
+#include "internal/platform/runnable.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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/single_thread_executor.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/single_thread_executor.h
new file mode 100644
index 00000000000..2979672ddce
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/single_thread_executor.h
@@ -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 "internal/platform/implementation/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_
+
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/system_clock.cc b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/system_clock.cc
new file mode 100644
index 00000000000..db7c722ff88
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/system_clock.cc
@@ -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 "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.h
new file mode 100644
index 00000000000..fd864a13f80
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.h
@@ -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 "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.mm
new file mode 100644
index 00000000000..2dc5bf1525f
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/utils.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/utils.h"
+
+#import "absl/container/flat_hash_map.h"
+#include "absl/strings/string_view.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.h
new file mode 100644
index 00000000000..5a6198b1fd7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.h
@@ -0,0 +1,202 @@
+// 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 "internal/platform/implementation/wifi_lan.h"
+#include "internal/platform/nsd_service_info.h"
+#import "internal/platform/implementation/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_);
+
+ absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange() override {
+ return absl::nullopt;
+ }
+
+ 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_
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.mm
new file mode 100644
index 00000000000..debaa4cfaa7
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Platform/wifi_lan.mm
@@ -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 "internal/platform/implementation/ios/Source/Platform/wifi_lan.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/internal/common.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/synchronization/mutex.h"
+#include "internal/platform/cancellation_flag.h"
+#include "internal/platform/exception.h"
+#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h"
+#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
+#include "internal/platform/implementation/ios/Source/Platform/utils.h"
+#include "internal/platform/implementation/wifi_lan.h"
+#include "internal/platform/nsd_service_info.h"
+#include "internal/platform/prng.h"
+#import "GoogleToolboxForMac/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/BUILD b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/BUILD
new file mode 100644
index 00000000000..1ad2cda37f2
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/BUILD
@@ -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 = ["//internal/platform/implementation/ios:__subpackages__"])
+
+objc_library(
+ name = "Shared",
+ srcs = [
+ "GNCUtils.m",
+ ],
+ hdrs = [
+ "GNCUtils.h",
+ ],
+ deps = [
+ "//third_party/objective_c/google_toolbox_for_mac:GTM_StringEncoding",
+ "@aappleby_smhasher//:libmurmur3",
+ ],
+)
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.h b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.h
new file mode 100644
index 00000000000..0d27c550978
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.h
@@ -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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.m b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.m
new file mode 100644
index 00000000000..ac2bba83386
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Source/Shared/GNCUtils.m
@@ -0,0 +1,65 @@
+// 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 "internal/platform/implementation/ios/Source/Shared/GNCUtils.h"
+
+#import <CommonCrypto/CommonDigest.h>
+
+#import "GoogleToolboxForMac/GTMStringEncoding.h"
+
+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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/BUILD b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/BUILD
new file mode 100644
index 00000000000..4b09d7d57bb
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/BUILD
@@ -0,0 +1,63 @@
+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 = [
+ "//internal/platform/implementation/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 = [
+ "//internal/platform/implementation/ios/Source/Platform",
+ "//internal/platform/implementation/ios/Source/Shared",
+ ],
+)
+
+ios_unit_test(
+ name = "SharedTests",
+ minimum_os_version = "12.0",
+ runner = "//testing/utp/ios:IOS_12",
+ deps = [
+ ":SharedTestslib",
+ ],
+)
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCCryptoTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCCryptoTest.mm
new file mode 100644
index 00000000000..3273325bb72
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCCryptoTest.mm
@@ -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 "internal/platform/implementation/crypto.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCInputFileTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCInputFileTest.mm
new file mode 100644
index 00000000000..e744de067a1
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCInputFileTest.mm
@@ -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 "internal/platform/byte_array.h"
+#import "internal/platform/exception.h"
+#import "internal/platform/implementation/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCMultiThreadExecutorTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCMultiThreadExecutorTest.mm
new file mode 100644
index 00000000000..c5439fcffe5
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCMultiThreadExecutorTest.mm
@@ -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 "absl/time/time.h"
+#include "internal/platform/implementation/cancelable.h"
+#include "internal/platform/implementation/executor.h"
+#include "internal/platform/implementation/platform.h"
+#include "internal/platform/implementation/submittable_executor.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCScheduledExecutorTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCScheduledExecutorTest.mm
new file mode 100644
index 00000000000..9f00bc47391
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCScheduledExecutorTest.mm
@@ -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 "absl/time/time.h"
+#include "internal/platform/implementation/cancelable.h"
+#include "internal/platform/implementation/executor.h"
+#include "internal/platform/implementation/platform.h"
+#include "internal/platform/implementation/scheduled_executor.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCSingleThreadExecutorTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCSingleThreadExecutorTest.mm
new file mode 100644
index 00000000000..31686f804ab
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Platform/GNCSingleThreadExecutorTest.mm
@@ -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 "absl/time/time.h"
+#include "internal/platform/implementation/cancelable.h"
+#include "internal/platform/implementation/executor.h"
+#include "internal/platform/implementation/platform.h"
+#include "internal/platform/implementation/submittable_executor.h"
+#include "internal/platform/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
diff --git a/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Shared/GNCUtilsTest.mm b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Shared/GNCUtilsTest.mm
new file mode 100644
index 00000000000..6773f10f5d9
--- /dev/null
+++ b/chromium/third_party/nearby/src/internal/platform/implementation/ios/Tests/Shared/GNCUtilsTest.mm
@@ -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 "internal/platform/byte_array.h"
+#include "internal/platform/implementation/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