ios: nearbyConnections: Move source to //third_party.

PiperOrigin-RevId: 408046875
This commit is contained in:
edwinwu
2021-11-06 09:45:54 -07:00
committed by Copybara-Service
parent 8c2dd35eac
commit fb0337ebfa
76 changed files with 6192 additions and 7 deletions
@@ -0,0 +1,93 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"])
package(default_visibility = ["//platform/impl/ios:__subpackages__"])
objc_library(
name = "Platform",
srcs = [
"crypto.mm",
"input_file.mm",
"log_message.mm",
"multi_thread_executor.mm",
"scheduled_executor.mm",
"utils.mm",
"wifi_lan.mm",
],
hdrs = [
"input_file.h",
"log_message.h",
"multi_thread_executor.h",
"scheduled_executor.h",
"single_thread_executor.h",
"utils.h",
"wifi_lan.h",
],
sdk_frameworks = [
"CoreBluetooth",
"CoreFoundation",
],
deps = [
":Platform_cc",
"//platform/api:platform",
"//platform/api:types",
"//platform/impl/ios/Source/Mediums",
"//platform/impl/ios/Source/Shared",
"//platform/impl/shared:file",
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
],
)
cc_library(
name = "Platform_cc",
srcs = [
"condition_variable.cc",
"count_down_latch.cc",
"system_clock.cc",
],
hdrs = [
"atomic_boolean.h",
"atomic_uint32.h",
"condition_variable.h",
"count_down_latch.h",
"mutex.h",
],
deps = [
"//absl/strings:str_format",
"//absl/synchronization",
"//absl/time",
"//platform/api:platform",
"//platform/api:types",
],
)
cc_test(
name = "Platform_cc_test",
srcs = [
"atomic_boolean_test.cc",
"atomic_uint32_test.cc",
"condition_variable_test.cc",
"count_down_latch_test.cc",
"mutex_test.cc",
],
shard_count = 16,
deps = [
":Platform_cc",
"//testing/base/public:gunit_main",
"//absl/synchronization",
"//absl/time",
"//thread/fiber",
],
)
@@ -0,0 +1,46 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
#define PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
#include <atomic>
#include "platform/api/atomic_boolean.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete AtomicBoolean implementation.
class AtomicBoolean : public api::AtomicBoolean {
public:
explicit AtomicBoolean(bool initial_value) : value_(initial_value) {}
~AtomicBoolean() override = default;
AtomicBoolean(const AtomicBoolean&) = delete;
AtomicBoolean& operator=(const AtomicBoolean&) = delete;
bool Get() const override { return value_.load(); }
bool Set(bool value) override { return value_.exchange(value); }
private:
std::atomic_bool value_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
@@ -0,0 +1,78 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/atomic_boolean.h"
#include "gtest/gtest.h"
#include "thread/fiber/fiber.h"
namespace location {
namespace nearby {
namespace ios {
namespace {
TEST(AtomicBooleanTest, SetOnSameThread) {
AtomicBoolean atomic_boolean_{false};
EXPECT_EQ(false, atomic_boolean_.Get());
atomic_boolean_.Set(true);
EXPECT_EQ(true, atomic_boolean_.Get());
}
TEST(AtomicBooleanTest, MultipleSetGetOnSameThread) {
AtomicBoolean atomic_boolean_{false};
EXPECT_EQ(false, atomic_boolean_.Get());
atomic_boolean_.Set(true);
EXPECT_EQ(true, atomic_boolean_.Get());
atomic_boolean_.Set(true);
EXPECT_EQ(true, atomic_boolean_.Get());
atomic_boolean_.Set(false);
EXPECT_EQ(false, atomic_boolean_.Get());
atomic_boolean_.Set(true);
EXPECT_EQ(true, atomic_boolean_.Get());
}
TEST(AtomicBooleanTest, SetOnNewThread) {
AtomicBoolean atomic_boolean_{false};
EXPECT_EQ(false, atomic_boolean_.Get());
thread::Fiber f([&] { atomic_boolean_.Set(true); });
f.Join();
EXPECT_EQ(true, atomic_boolean_.Get());
}
TEST(AtomicBooleanTest, GetOnNewThread) {
AtomicBoolean atomic_boolean_{false};
EXPECT_EQ(false, atomic_boolean_.Get());
atomic_boolean_.Set(true);
EXPECT_EQ(true, atomic_boolean_.Get());
thread::Fiber f([&] { EXPECT_EQ(true, atomic_boolean_.Get()); });
f.Join();
}
} // namespace
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
#define PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
#include <atomic>
#include <cstdint>
#include "platform/api/atomic_reference.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete AtomicUint32 implementation.
class AtomicUint32 : public api::AtomicUint32 {
public:
explicit AtomicUint32(std::uint32_t initial_value) : value_(initial_value) {}
~AtomicUint32() override = default;
AtomicUint32(const AtomicUint32&) = delete;
AtomicUint32& operator=(const AtomicUint32&) = delete;
std::uint32_t Get() const override { return value_; }
void Set(std::uint32_t value) override { value_ = value; }
private:
std::atomic<std::uint32_t> value_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
@@ -0,0 +1,64 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/atomic_uint32.h"
#include "gtest/gtest.h"
#include "thread/fiber/fiber.h"
namespace location {
namespace nearby {
namespace ios {
namespace {
TEST(AtomicUint32Test, GetOnSameThread) {
std::uint32_t initial_value = 1450;
AtomicUint32 atomic_reference_{initial_value};
EXPECT_EQ(initial_value, atomic_reference_.Get());
}
TEST(AtomicUint32Test, SetGetOnSameThread) {
std::uint32_t initial_value_ = 1450;
AtomicUint32 atomic_reference_{initial_value_};
std::uint32_t new_value = 28;
atomic_reference_.Set(new_value);
EXPECT_EQ(new_value, atomic_reference_.Get());
}
TEST(AtomicUint32Test, SetOnNewThread) {
std::uint32_t initial_value_ = 1450;
AtomicUint32 atomic_reference_{initial_value_};
std::uint32_t new_thread_value = 28;
thread::Fiber f([&] { atomic_reference_.Set(new_thread_value); });
f.Join();
EXPECT_EQ(new_thread_value, atomic_reference_.Get());
}
TEST(AtomicUint32Test, GetOnNewThread) {
std::uint32_t initial_value_ = 1450;
AtomicUint32 atomic_reference_{initial_value_};
std::uint32_t new_value = 28;
atomic_reference_.Set(new_value);
thread::Fiber f([&] { EXPECT_EQ(new_value, atomic_reference_.Get()); });
f.Join();
}
} // namespace
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,37 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/condition_variable.h"
#include "platform/impl/ios/Source/Platform/mutex.h"
namespace location {
namespace nearby {
namespace ios {
Exception ConditionVariable::Wait() {
condition_variable_.Wait(mutex_);
return {Exception::kSuccess};
}
Exception ConditionVariable::Wait(absl::Duration timeout) {
condition_variable_.WaitWithTimeout(mutex_, timeout);
return {Exception::kSuccess};
}
void ConditionVariable::Notify() { condition_variable_.SignalAll(); }
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,48 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
#define PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
#include "absl/synchronization/mutex.h"
#include "platform/api/condition_variable.h"
#include "platform/impl/ios/Source/Platform/mutex.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete ConditionVariable implementation.
class ConditionVariable : public api::ConditionVariable {
public:
explicit ConditionVariable(ios::Mutex* mutex) : mutex_(&mutex->mutex_) {}
~ConditionVariable() override = default;
ConditionVariable(const ConditionVariable&) = delete;
ConditionVariable& operator=(const ConditionVariable&) = delete;
Exception Wait() override;
Exception Wait(absl::Duration timeout) override;
void Notify() override;
private:
absl::Mutex* mutex_;
absl::CondVar condition_variable_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
@@ -0,0 +1,84 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/condition_variable.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "platform/impl/ios/Source/Platform/mutex.h"
#include "thread/fiber/fiber.h"
namespace location {
namespace nearby {
namespace ios {
namespace {
TEST(ConditionVariableTest, CanCreate) {
Mutex mutex{};
ConditionVariable cond{&mutex};
}
TEST(ConditionVariableTest, CanWakeupWaiter) {
Mutex mutex{};
ConditionVariable cond{&mutex};
bool done = false;
bool waiting = false;
{
thread::Fiber f([&cond, &mutex, &done, &waiting] {
mutex.Lock();
waiting = true;
cond.Wait();
waiting = false;
done = true;
mutex.Unlock();
});
while (true) {
{
mutex.Lock();
if (waiting) {
mutex.Unlock();
break;
}
mutex.Unlock();
}
absl::SleepFor(absl::Milliseconds(100));
}
{
mutex.Lock();
cond.Notify();
EXPECT_FALSE(done);
mutex.Unlock();
}
f.Join();
}
EXPECT_TRUE(done);
}
TEST(ConditionVariableTest, WaitTerminatesOnTimeoutWithoutNotify) {
Mutex mutex{};
ConditionVariable cond{&mutex};
mutex.Lock();
const absl::Duration kWaitTime = absl::Milliseconds(100);
absl::Time start = absl::Now();
cond.Wait(kWaitTime);
absl::Duration duration = absl::Now() - start;
EXPECT_GE(duration, kWaitTime);
mutex.Unlock();
}
} // namespace
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,40 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/count_down_latch.h"
namespace location {
namespace nearby {
namespace ios {
Exception CountDownLatch::Await() {
absl::MutexLock lock(&mutex_, absl::Condition(IsZeroOrNegative, &count_));
return {Exception::kSuccess};
}
ExceptionOr<bool> CountDownLatch::Await(absl::Duration timeout) {
bool condition = mutex_.LockWhenWithTimeout(
absl::Condition(IsZeroOrNegative, &count_), timeout);
mutex_.Unlock();
return ExceptionOr<bool>(condition);
}
void CountDownLatch::CountDown() {
absl::MutexLock lock(&mutex_);
count_--;
}
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,49 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
#define PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
#include "absl/synchronization/mutex.h"
#include "platform/api/count_down_latch.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete CountDownLatch implementation.
class CountDownLatch : public api::CountDownLatch {
public:
explicit CountDownLatch(int count) : count_(count) {}
~CountDownLatch() override = default;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
Exception Await() override;
ExceptionOr<bool> Await(absl::Duration timeout) override;
void CountDown() override;
private:
static bool IsZeroOrNegative(int* count) { return 0 >= *count; }
absl::Mutex mutex_;
int count_ ABSL_GUARDED_BY(mutex_);
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
@@ -0,0 +1,83 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/count_down_latch.h"
#include "gtest/gtest.h"
#include "thread/fiber/fiber.h"
namespace location {
namespace nearby {
namespace ios {
namespace {
TEST(CountDownLatchTest, LatchAwaitCanWait) {
CountDownLatch latch(1);
std::atomic_bool done = false;
thread::Fiber f([&done, &latch] {
done = true;
latch.CountDown();
});
f.Join();
latch.Await();
EXPECT_TRUE(done);
}
TEST(CountDownLatchTest, LatchExtraCountDownIgnored) {
CountDownLatch latch(1);
std::atomic_bool done = false;
thread::Fiber f([&done, &latch] {
done = true;
latch.CountDown();
latch.CountDown();
latch.CountDown();
});
f.Join();
latch.Await();
EXPECT_TRUE(done);
}
TEST(CountDownLatchTest, LatchAwaitWithTimeoutCanExpire) {
CountDownLatch latch(1);
auto response = latch.Await(absl::Milliseconds(100));
EXPECT_TRUE(response.ok());
EXPECT_FALSE(response.result());
}
TEST(CountDownLatchTest, InitialCountZero_AwaitDoesNotBlock) {
CountDownLatch latch(0);
auto response = latch.Await();
EXPECT_TRUE(response.Ok());
}
TEST(CountDownLatchTest, InitialCountNegative_AwaitDoesNotBlock) {
CountDownLatch latch(-1);
auto response = latch.Await();
EXPECT_TRUE(response.Ok());
}
} // namespace
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,39 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "third_party/nearby_connections/cpp/platform/api/crypto.h"
#import "third_party/absl/strings/string_view.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Shared/GNCUtils.h"
namespace location {
namespace nearby {
void Crypto::Init() {}
ByteArray Crypto::Md5(absl::string_view input) {
if (input.empty()) return ByteArray();
return ByteArrayFromNSData(GNCMd5String(ObjCStringFromCppString(input)));
}
ByteArray Crypto::Sha256(absl::string_view input) {
if (input.empty()) return ByteArray();
return ByteArrayFromNSData(GNCSha256String(ObjCStringFromCppString(input)));
}
} // namespace nearby
} // namespace location
@@ -0,0 +1,48 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_INPUT_FILE_H_
#define PLATFORM_IMPL_IOS_INPUT_FILE_H_
#import <Foundation/Foundation.h>
#include "platform/api/input_file.h"
namespace location {
namespace nearby {
namespace ios {
/** This InputFile subclass takes input from an NSURL. */
class InputFile : public api::InputFile {
public:
explicit InputFile(NSURL *nsURL);
~InputFile() override = default;
InputFile(InputFile &&) = default;
InputFile &operator=(InputFile &&) = default;
ExceptionOr<ByteArray> Read(std::int64_t size) override;
std::string GetFilePath() const override;
std::int64_t GetTotalSize() const override;
Exception Close() override;
private:
NSURL *nsURL_;
NSInputStream *nsStream_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_INPUT_FILE_H_
@@ -0,0 +1,69 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/input_file.h"
#include <string>
#import "third_party/nearby_connections/cpp/platform/base/exception.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
namespace location {
namespace nearby {
namespace ios {
InputFile::InputFile(NSURL *nsURL) : nsURL_(nsURL) {
std::string string = CppStringFromObjCString([nsURL_ absoluteString]);
nsStream_ = [NSInputStream inputStreamWithURL:nsURL_];
[nsStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[nsStream_ open];
}
ExceptionOr<ByteArray> InputFile::Read(std::int64_t size) {
uint8_t *bytes_read = new uint8_t[size];
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
NSInteger numberOfBytesRead = [nsStream_ read:bytes_read maxLength:numberOfBytesToRead];
if (numberOfBytesRead == 0) {
// Reached end of stream.
return ExceptionOr<ByteArray>();
} else if (numberOfBytesRead < 0) {
// Stream error.
return ExceptionOr<ByteArray>(Exception::kIo);
}
return ExceptionOr<ByteArray>(ByteArrayFromNSData([NSData dataWithBytes:bytes_read
length:numberOfBytesRead]));
}
std::string InputFile::GetFilePath() const {
return CppStringFromObjCString([nsURL_ absoluteString]);
}
std::int64_t InputFile::GetTotalSize() const {
NSNumber *fileSizeValue = nil;
BOOL result = [nsURL_ getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
if (result) {
return fileSizeValue.longValue;
} else {
return 0;
}
}
Exception InputFile::Close() {
[nsStream_ close];
return {Exception::kSuccess};
}
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
#define PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
#include "base/check.h"
#include "platform/api/log_message.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete LogMessage implementation
class LogMessage : public api::LogMessage {
public:
LogMessage(const char* file, int line, Severity severity);
~LogMessage() override = default;
LogMessage(const LogMessage&) = delete;
LogMessage& operator=(const LogMessage&) = delete;
void Print(const char* format, ...) override;
std::ostream& Stream() override;
private:
absl::LogStreamer log_streamer_;
api::LogMessage::Severity severity_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // IPHONE_SHARED_NEARBY_CONNECTIONS_SOURCE_PLATFORM_LOG_MESSAGE_H_
@@ -0,0 +1,87 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/log_message.h"
#include "base/logging.h"
#include "third_party/nearby_connections/cpp/platform/api/log_message.h"
#include "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
namespace location {
namespace nearby {
namespace ios {
api::LogMessage::Severity gMinLogSeverity = api::LogMessage::Severity::kInfo;
GTMLoggerLevel ConvertSeverity(api::LogMessage::Severity severity) {
switch (severity) {
case api::LogMessage::Severity::kVerbose:
return kGTMLoggerLevelDebug;
case api::LogMessage::Severity::kInfo:
return kGTMLoggerLevelInfo;
case api::LogMessage::Severity::kWarning:
return kGTMLoggerLevelInfo;
case api::LogMessage::Severity::kError:
return kGTMLoggerLevelError;
case api::LogMessage::Severity::kFatal:
return kGTMLoggerLevelAssert;
}
}
LogMessage::LogMessage(const char* file, int line, Severity severity)
: log_streamer_(ConvertSeverity(severity), file, line), severity_(severity) {}
void LogMessage::Print(const char* format, ...) {
va_list ap;
va_start(ap, format);
switch (ConvertSeverity(severity_)) {
case kGTMLoggerLevelDebug:
[[GTMLogger sharedLogger] logDebug:[NSString stringWithUTF8String:format], ap];
break;
case kGTMLoggerLevelInfo:
[[GTMLogger sharedLogger] logInfo:[NSString stringWithUTF8String:format], ap];
break;
case kGTMLoggerLevelError:
[[GTMLogger sharedLogger] logError:[NSString stringWithUTF8String:format], ap];
break;
case kGTMLoggerLevelAssert:
[[GTMLogger sharedLogger] logAssert:[NSString stringWithUTF8String:format], ap];
break;
case kGTMLoggerLevelUnknown:
// no-op
break;
}
}
// TODO(b/169292092): GTMLogger doesn't support stream. Temporarily use absl LogStreamer to make
// build pass.
std::ostream& LogMessage::Stream() { return log_streamer_.stream(); }
} // namespace ios
namespace api {
// static
void LogMessage::SetMinLogSeverity(Severity severity) { ios::gMinLogSeverity = severity; }
// static
bool LogMessage::ShouldCreateLogMessage(Severity severity) {
// TODO(b/169292092): GTMLogger doesn't support stream which cause crash. Temporarily turn off
// LogMessage.
return false;
}
} // namespace api
} // namespace nearby
} // namespace location
@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
#define PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
#include "platform/api/submittable_executor.h"
#import "third_party/nearby_connections/cpp/platform/base/runnable.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
namespace location {
namespace nearby {
namespace ios {
class MultiThreadExecutor : public api::SubmittableExecutor {
public:
explicit MultiThreadExecutor(int max_concurrency);
~MultiThreadExecutor() override = default;
MultiThreadExecutor(const MultiThreadExecutor&) = delete;
MultiThreadExecutor& operator=(const MultiThreadExecutor&) = delete;
// api::SubmittableExecutor:
void Shutdown() override;
void Execute(Runnable&& runnable) override;
bool DoSubmit(Runnable&& runnable) override;
private:
std::unique_ptr<ScheduledExecutor> scheduled_executor_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_MULTI_THREAD_EXECUTOR_H_
@@ -0,0 +1,40 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/multi_thread_executor.h"
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
namespace location {
namespace nearby {
namespace ios {
MultiThreadExecutor::MultiThreadExecutor(int max_concurrency) {
scheduled_executor_ = std::make_unique<ScheduledExecutor>(max_concurrency);
}
void MultiThreadExecutor::Shutdown() { scheduled_executor_->Shutdown(); }
void MultiThreadExecutor::Execute(Runnable&& runnable) {
scheduled_executor_->Execute(std::move(runnable));
}
bool MultiThreadExecutor::DoSubmit(Runnable&& runnable) {
return scheduled_executor_->DoSubmit(std::move(runnable));
}
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,84 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_MUTEX_H_
#define PLATFORM_IMPL_IOS_MUTEX_H_
#include "absl/synchronization/mutex.h"
#include "platform/api/mutex.h"
namespace location {
namespace nearby {
namespace ios {
// Concrete Mutex implementation.
class ABSL_LOCKABLE Mutex : public api::Mutex {
public:
explicit Mutex() {}
~Mutex() override = default;
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
mutex_.Lock();
mutex_.ForgetDeadlockInfo();
}
void Unlock() ABSL_UNLOCK_FUNCTION() override { mutex_.Unlock(); }
private:
friend class ConditionVariable;
absl::Mutex mutex_;
};
class ABSL_LOCKABLE RecursiveMutex : public api::Mutex {
public:
RecursiveMutex() = default;
~RecursiveMutex() override = default;
RecursiveMutex(RecursiveMutex&&) = delete;
RecursiveMutex& operator=(RecursiveMutex&&) = delete;
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
intptr_t thread_id = ThreadId();
if (thread_id_.load(std::memory_order_acquire) != thread_id) {
mutex_.Lock();
thread_id_.store(thread_id, std::memory_order_release);
}
++count_;
}
void Unlock() ABSL_UNLOCK_FUNCTION() override {
if (--count_ == 0) {
thread_id_.store(0, std::memory_order_release);
mutex_.Unlock();
}
}
private:
static inline intptr_t ThreadId() {
ABSL_CONST_INIT thread_local int per_thread = 0;
return reinterpret_cast<intptr_t>(&per_thread);
}
std::atomic<intptr_t> thread_id_{0};
int count_{0};
absl::Mutex mutex_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_MUTEX_H_
@@ -0,0 +1,92 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/ios/Source/Platform/mutex.h"
#include "gtest/gtest.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "thread/fiber/fiber.h"
namespace location {
namespace nearby {
namespace ios {
namespace {
static const absl::Duration kTimeToWait = absl::Milliseconds(500);
TEST(MutexTest, LockOnce_UnlockOnce) {
Mutex test_mutex_1{};
test_mutex_1.Lock();
test_mutex_1.Unlock();
RecursiveMutex test_mutex_2;
test_mutex_2.Lock();
test_mutex_2.Unlock();
}
TEST(MutexTest, BasicLockingWorks) {
absl::Notification lock_obtained;
Mutex test_mutex{};
test_mutex.Lock();
thread::Fiber f([&test_mutex, &lock_obtained] {
test_mutex.Lock();
test_mutex.Unlock();
lock_obtained.Notify();
});
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
test_mutex.Unlock();
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
f.Join();
}
TEST(MutexTest, RecursiveLockingWorks) {
absl::Notification lock_obtained;
RecursiveMutex test_mutex;
test_mutex.Lock();
thread::Fiber f([&test_mutex, &lock_obtained] {
test_mutex.Lock();
test_mutex.Unlock();
lock_obtained.Notify();
});
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
test_mutex.Unlock();
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
f.Join();
}
TEST(MutexTest, RecursiveLockingForNestedWorks) {
absl::Notification lock_obtained;
RecursiveMutex test_mutex;
test_mutex.Lock();
thread::Fiber f([&test_mutex, &lock_obtained]()
ABSL_NO_THREAD_SAFETY_ANALYSIS {
test_mutex.Lock();
test_mutex.Lock();
test_mutex.Unlock();
test_mutex.Unlock();
lock_obtained.Notify();
});
EXPECT_FALSE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
test_mutex.Unlock();
EXPECT_TRUE(lock_obtained.WaitForNotificationWithTimeout(kTimeToWait));
f.Join();
}
} // namespace
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,68 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
#define PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
#import <Foundation/Foundation.h>
#include <functional>
#include <memory>
#include "platform/api/scheduled_executor.h"
#include "platform/base/runnable.h"
/**
* The impl class is an Obj-C class so that
* (a) the dispatch block can strongly retain it, and
* (b) for ease of declaring an atomic property.
*/
@interface GNCOperationQueueImpl : NSObject
@property(nonatomic) NSOperationQueue* queue;
@property(atomic) BOOL shuttingDown;
@end
namespace location {
namespace nearby {
namespace ios {
// Concrete ScheduledExecutor implementation.
class ScheduledExecutor : public api::ScheduledExecutor {
public:
// The max_concurrency = 1 for default constructor.
ScheduledExecutor();
explicit ScheduledExecutor(int max_concurrency);
~ScheduledExecutor() override;
ScheduledExecutor(const ScheduledExecutor&) = delete;
ScheduledExecutor& operator=(const ScheduledExecutor&) = delete;
// api::ScheduledExecutor:
void Shutdown() override;
std::shared_ptr<api::Cancelable> Schedule(Runnable&& runnable, absl::Duration duration) override;
void Execute(Runnable&& runnable) override;
bool DoSubmit(Runnable&& runnable);
private:
void Shutdown(std::int64_t timeout_millis);
GNCOperationQueueImpl* impl_;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_SCHEDULED_EXECUTOR_H_
@@ -0,0 +1,144 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/scheduled_executor.h"
#import <Foundation/Foundation.h>
#include "third_party/absl/time/time.h"
#include "third_party/nearby_connections/cpp/platform/base/runnable.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/atomic_boolean.h"
// This wraps the C++ Runnable in an Obj-C object for memory management. It is retained by the
// dispatch block below, and deleted when the block is released.
@interface GNCRunnableWrapper : NSObject {
@public
location::nearby::Runnable _runnable;
std::unique_ptr<location::nearby::ios::AtomicBoolean> _canceled;
}
@end
@implementation GNCRunnableWrapper
+ (instancetype)wrapperWithRunnable:(location::nearby::Runnable)runnable {
GNCRunnableWrapper *wrapper = [[GNCRunnableWrapper alloc] init];
wrapper->_runnable = runnable;
wrapper->_canceled = std::make_unique<location::nearby::ios::AtomicBoolean>(false);
return wrapper;
}
@end
@implementation GNCOperationQueueImpl
+ (instancetype)implWithMaxConcurrency:(int)maxConcurrency {
GNCOperationQueueImpl *impl = [[GNCOperationQueueImpl alloc] init];
impl.queue = [[NSOperationQueue alloc] init];
impl.queue.maxConcurrentOperationCount = maxConcurrency;
return impl;
}
@end
namespace location {
namespace nearby {
namespace ios {
static const std::int64_t kExecutorShutdownDefaultTimeout = 500; // 0.5 seconds
// This Cancelable references a Runnable and a cancel method that sets its canceled boolean to true.
class CancelableForRunnable : public api::Cancelable {
public:
explicit CancelableForRunnable(GNCRunnableWrapper *runnable) : runnable_(runnable) {}
CancelableForRunnable() = default;
~CancelableForRunnable() override = default;
CancelableForRunnable(const CancelableForRunnable &) = delete;
CancelableForRunnable &operator=(const CancelableForRunnable &) = delete;
// api::Cancelable:
bool Cancel() override {
runnable_->_canceled->Set(true);
return true;
}
private:
GNCRunnableWrapper *runnable_;
};
ScheduledExecutor::ScheduledExecutor() { impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:1]; }
ScheduledExecutor::ScheduledExecutor(int max_concurrency) {
impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:max_concurrency];
}
ScheduledExecutor::~ScheduledExecutor() { impl_ = nil; }
void ScheduledExecutor::Shutdown() { Shutdown(kExecutorShutdownDefaultTimeout); }
std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable,
absl::Duration duration) {
if (impl_.shuttingDown) return std::shared_ptr<api::Cancelable>(nullptr);
// Wrap the runnable in an Obj-C object so it can be referenced by the delayed block.
GNCRunnableWrapper *wrapper = [GNCRunnableWrapper wrapperWithRunnable:std::move(runnable)];
CancelableForRunnable *cancelable = new CancelableForRunnable(wrapper);
GNCOperationQueueImpl *impl = impl_; // don't capture |this|
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, absl::ToInt64Milliseconds(duration) * NSEC_PER_MSEC),
dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0), ^{
[impl.queue addOperationWithBlock:^{
// Execute the runnable only if the executor is not shutting down, and the runnable isn't
// canceled.
// Warning: This block should reference only Obj-C objects, and never C++ objects.
if (!impl.shuttingDown && !wrapper->_canceled->Get()) {
wrapper->_runnable();
}
}];
});
return std::shared_ptr<api::Cancelable>(cancelable);
}
void ScheduledExecutor::Execute(Runnable &&runnable) {
DoSubmit(std::move(runnable));
}
bool ScheduledExecutor::DoSubmit(Runnable &&runnable) {
if (impl_.shuttingDown) {
return false;
}
// Submit the runnable to the queue.
Runnable local_runnable = std::move(runnable);
[impl_.queue addOperationWithBlock:^{
local_runnable();
}];
return true;
}
void ScheduledExecutor::Shutdown(std::int64_t timeout_millis) {
// Prevent new/delayed operations from being queued/executed.
impl_.shuttingDown = YES;
// Block until either (a) all currently executing operations finish, or (b) the timeout expires.
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0), ^{
[impl_.queue waitUntilAllOperationsAreFinished];
});
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, timeout_millis * NSEC_PER_MSEC));
}
} // namespace ios
} // namespace nearby
} // namespace location
@@ -0,0 +1,35 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
#define PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/multi_thread_executor.h"
namespace location {
namespace nearby {
namespace ios {
class SingleThreadExecutor : public MultiThreadExecutor {
public:
SingleThreadExecutor() : MultiThreadExecutor(1) {}
~SingleThreadExecutor() override = default;
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_SINGLE_THREAD_EXECUTOR_H_
@@ -0,0 +1,33 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/api/system_clock.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
void SystemClock::Init() {}
absl::Time SystemClock::ElapsedRealtime() { return absl::Now(); }
// TODO(b/169292092): Check the iOS primitive implementation for SystemClock.
Exception SystemClock::Sleep(absl::Duration duration) {
absl::SleepFor(duration);
return {Exception::kSuccess};
}
} // namespace nearby
} // namespace location
@@ -0,0 +1,74 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <set>
#include <string>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "platform/base/byte_array.h"
NS_ASSUME_NONNULL_BEGIN
namespace location {
namespace nearby {
/// Converts an Obj-C BOOL to a C++ bool.
bool CppBoolFromObjCBool(BOOL b);
/// Converts NSNumber to a char.
char CharFromNSNumber(NSNumber *n);
/// Converts a C++ string to an Obj-C string.
NSString *ObjCStringFromCppString(absl::string_view s);
/// Converts an Obj-C string to a C++ string.
std::string CppStringFromObjCString(NSString *s);
/// Converts ByteArray to NSData.
NSData *NSDataFromByteArray(ByteArray byteArray);
/// Converts NSData to ByteArray.
ByteArray ByteArrayFromNSData(NSData *data);
/// Converts NSUUID to a C++ string (representing a UUID).
std::string UUIDStringFromNSUUID(NSUUID *uuid);
/// Converts a C++ string (representing a Bluetooth UUID) to CBUUID.
CBUUID *CBUUIDFromBluetoothUUIDString(absl::string_view bluetoothUUID);
/// Converts CBUUID to a C++ string (representing a Bluetooth UUID).
std::string BluetoothUUIDStringFromCBUUID(CBUUID *bluetoothUUID);
/// Converts a C++ set of strings (representing Bluetooth UUIDs) to an NSSet of CBUUID.
NSSet<CBUUID *> *CBUUIDSetFromBluetoothUUIDStringSet(const std::set<std::string> &bluetoothUUIDSet);
/// Converts an NSSet of CBUUID to a C++ set of strings (representing Bluetooth UUIDs).
std::set<std::string> BluetoothUUIDStringSetFromCBUUIDSet(NSSet<CBUUID *> *bluetoothUUIDSet);
/// Converts a C++ TxtRecord to an Obj-C TxtRecord.
NSDictionary<NSString *, NSData *> *NSDictionaryFromCppTxtRecords(
const absl::flat_hash_map<std::string, std::string> &txt_records);
/// Converts an Obj-C TxtRecord to a C++ TxtRecord.
absl::flat_hash_map<std::string, std::string> AbslHashMapFromObjCTxtRecords(
NSDictionary<NSString *, NSData *> *txtRecords);
} // namespace nearby
} // namespace location
NS_ASSUME_NONNULL_END
@@ -0,0 +1,104 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
#import "third_party/absl/container/flat_hash_map.h"
#include "third_party/absl/strings/string_view.h"
#include "third_party/nearby_connections/cpp/platform/base/byte_array.h"
NS_ASSUME_NONNULL_BEGIN
namespace location {
namespace nearby {
bool CppBoolFromObjCBool(BOOL b) { return b ? true : false; }
char CharFromNSNumber(NSNumber* n) { return n.charValue; }
NSString* ObjCStringFromCppString(absl::string_view s) {
return [NSString stringWithUTF8String:s.data()];
}
std::string CppStringFromObjCString(NSString* s) {
return std::string([s UTF8String], [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
}
NSData* NSDataFromByteArray(ByteArray byteArray) {
return [NSData dataWithBytes:byteArray.data() length:byteArray.size()];
}
ByteArray ByteArrayFromNSData(NSData* data) {
return ByteArray((const char*)data.bytes, data.length);
}
std::string UUIDStringFromNSUUID(NSUUID* uuid) { return CppStringFromObjCString(uuid.UUIDString); }
CBUUID* CBUUIDFromBluetoothUUIDString(absl::string_view bluetoothUUID) {
return [CBUUID UUIDWithString:ObjCStringFromCppString(bluetoothUUID)];
}
std::string BluetoothUUIDStringFromCBUUID(CBUUID* bluetoothUUID) {
return CppStringFromObjCString(bluetoothUUID.UUIDString);
}
NSSet<CBUUID*>* CBUUIDSetFromBluetoothUUIDStringSet(const std::set<std::string>& bluetoothUUIDSet) {
NSMutableSet<CBUUID*>* objcBluetoothUUIDSet = [NSMutableSet set];
for (std::set<std::string>::const_iterator it = bluetoothUUIDSet.begin();
it != bluetoothUUIDSet.end(); ++it) {
[objcBluetoothUUIDSet addObject:CBUUIDFromBluetoothUUIDString(*it)];
}
return objcBluetoothUUIDSet;
}
std::set<std::string> BluetoothUUIDStringSetFromCBUUIDSet(NSSet<CBUUID*>* bluetoothUUIDSet) {
std::set<std::string> cppBluetoothUUIDSet;
for (CBUUID* bluetoothUUID in bluetoothUUIDSet) {
cppBluetoothUUIDSet.insert(BluetoothUUIDStringFromCBUUID(bluetoothUUID));
}
return cppBluetoothUUIDSet;
}
NSDictionary<NSString*, NSData*>* NSDictionaryFromCppTxtRecords(
const absl::flat_hash_map<std::string, std::string>& txt_records) {
NSMutableArray<NSString*>* keyArray = [[NSMutableArray alloc] init];
NSMutableArray<NSData*>* valueArray = [[NSMutableArray alloc] init];
for (auto it = txt_records.begin(); it != txt_records.end(); it++) {
NSString* key = @(it->first.c_str());
NSString* value = @(it->second.c_str());
[keyArray addObject:key];
[valueArray addObject:[value dataUsingEncoding:NSUTF8StringEncoding]];
}
NSDictionary<NSString*, NSData*>* dict = [[NSDictionary alloc] initWithObjects:valueArray
forKeys:keyArray];
return dict;
}
absl::flat_hash_map<std::string, std::string> AbslHashMapFromObjCTxtRecords(
NSDictionary<NSString*, NSData*>* txtRecords) {
absl::flat_hash_map<std::string, std::string> txt_record;
for (NSString* key in txtRecords) {
NSString* value = [[NSString alloc] initWithData:[txtRecords objectForKey:key]
encoding:NSUTF8StringEncoding];
txt_record.insert({CppStringFromObjCString(key), CppStringFromObjCString(value)});
}
return txt_record;
}
} // namespace nearby
} // namespace location
NS_ASSUME_NONNULL_END
@@ -0,0 +1,198 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_IOS_WIFI_LAN_H_
#define PLATFORM_IMPL_IOS_WIFI_LAN_H_
#import <Foundation/Foundation.h>
#include <string>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "platform/api/wifi_lan.h"
#include "platform/base/nsd_service_info.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h" // IWYU pragma: export
@class GNCMBonjourBrowser;
@class GNCMBonjourService;
namespace location {
namespace nearby {
namespace ios {
/** InputStream that reads from GNCMConnection. */
class WifiLanInputStream : public InputStream {
public:
WifiLanInputStream();
~WifiLanInputStream() override;
ExceptionOr<ByteArray> Read(std::int64_t size) override;
Exception Close() override;
GNCMConnectionHandlers* GetConnectionHandlers() { return connectionHandlers_; }
private:
GNCMConnectionHandlers* connectionHandlers_;
NSMutableArray<NSData*>* newDataPackets_;
NSMutableData* accumulatedData_;
NSCondition* condition_;
};
/** OutputStream that writes to GNCMConnection. */
class WifiLanOutputStream : public OutputStream {
public:
explicit WifiLanOutputStream(id<GNCMConnection> connection)
: connection_(connection), condition_([[NSCondition alloc] init]) {}
~WifiLanOutputStream() override;
Exception Write(const ByteArray& data) override;
Exception Flush() override;
Exception Close() override;
private:
id<GNCMConnection> connection_;
NSCondition* condition_;
};
/** Concrete WifiLanSocket implementation. */
class WifiLanSocket : public api::WifiLanSocket {
public:
WifiLanSocket() = default;
explicit WifiLanSocket(id<GNCMConnection> connection);
~WifiLanSocket() override;
// api::WifiLanSocket:
InputStream& GetInputStream() override { return *input_stream_; }
OutputStream& GetOutputStream() override { return *output_stream_; }
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
private:
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable absl::Mutex mutex_;
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
std::unique_ptr<WifiLanInputStream> input_stream_;
std::unique_ptr<WifiLanOutputStream> output_stream_;
};
/** Concrete WifiLanServerSocket implementation. */
class WifiLanServerSocket : public api::WifiLanServerSocket {
public:
static std::string GetName(const std::string& ip_address, int port);
~WifiLanServerSocket() override;
// api::WifiLanServerSocket:
std::string GetIPAddress() const override ABSL_LOCKS_EXCLUDED(mutex_) {
absl::MutexLock lock(&mutex_);
return ip_address_;
}
void SetIPAddress(const std::string& ip_address) ABSL_LOCKS_EXCLUDED(mutex_) {
absl::MutexLock lock(&mutex_);
ip_address_ = ip_address;
}
int GetPort() const override ABSL_LOCKS_EXCLUDED(mutex_) {
absl::MutexLock lock(&mutex_);
return port_;
}
void SetPort(int port) ABSL_LOCKS_EXCLUDED(mutex_) {
absl::MutexLock lock(&mutex_);
port_ = port;
}
std::unique_ptr<api::WifiLanSocket> Accept() override ABSL_LOCKS_EXCLUDED(mutex_);
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
bool Connect(std::unique_ptr<WifiLanSocket> socket) ABSL_LOCKS_EXCLUDED(mutex_);
void SetCloseNotifier(std::function<void()> notifier) ABSL_LOCKS_EXCLUDED(mutex_);
private:
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable absl::Mutex mutex_;
std::string ip_address_ ABSL_GUARDED_BY(mutex_);
int port_ ABSL_GUARDED_BY(mutex_);
absl::CondVar cond_;
absl::flat_hash_set<std::unique_ptr<WifiLanSocket>> pending_sockets_ ABSL_GUARDED_BY(mutex_);
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
/** Concrete WifiLanMedium implementation. */
class WifiLanMedium : public api::WifiLanMedium {
public:
WifiLanMedium() = default;
~WifiLanMedium() override;
WifiLanMedium(const WifiLanMedium&) = delete;
WifiLanMedium& operator=(const WifiLanMedium&) = delete;
// api::WifiLanMedium:
bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override ABSL_LOCKS_EXCLUDED(mutex_);
bool StartDiscovery(const std::string& service_type, DiscoveredServiceCallback callback) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool StopDiscovery(const std::string& service_type) override ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port) override
ABSL_LOCKS_EXCLUDED(mutex_);
private:
struct AdvertisingInfo {
bool Empty() const { return services.empty(); }
void Clear() { services.clear(); }
void Add(const std::string& service_type, GNCMBonjourService* service) {
services.insert({service_type, service});
}
void Remove(const std::string& service_type) { services.erase(service_type); }
bool Existed(const std::string& service_type) const { return services.contains(service_type); }
absl::flat_hash_map<std::string, GNCMBonjourService*> services;
};
struct DiscoveringInfo {
bool Empty() const { return services.empty(); }
void Clear() { services.clear(); }
void Add(const std::string& service_type, GNCMBonjourBrowser* browser) {
services.insert({service_type, browser});
}
void Remove(const std::string& service_type) { services.erase(service_type); }
bool Existed(const std::string& service_type) const { return services.contains(service_type); }
absl::flat_hash_map<std::string, GNCMBonjourBrowser*> services;
};
std::string GetFakeIPAddress() const;
int GetFakePort() const;
absl::Mutex mutex_;
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
int requesting_port_ = 0;
DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<std::string, WifiLanServerSocket*> server_sockets_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<std::string, GNCMConnectionRequester> connection_requesters_
ABSL_GUARDED_BY(mutex_);
};
} // namespace ios
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_IOS_WIFI_LAN_H_
@@ -0,0 +1,497 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/wifi_lan.h"
#include <memory>
#include <string>
#include <utility>
#include "third_party/absl/container/flat_hash_map.h"
#include "third_party/absl/container/internal/common.h"
#include "third_party/absl/memory/memory.h"
#include "third_party/absl/strings/str_cat.h"
#include "third_party/absl/strings/str_format.h"
#include "third_party/absl/synchronization/mutex.h"
#include "third_party/nearby_connections/cpp/platform/api/wifi_lan.h"
#include "third_party/nearby_connections/cpp/platform/base/cancellation_flag.h"
#include "third_party/nearby_connections/cpp/platform/base/exception.h"
#include "third_party/nearby_connections/cpp/platform/base/nsd_service_info.h"
#include "third_party/nearby_connections/cpp/platform/base/prng.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/GNCMConnection.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h"
#import "third_party/nearby_connections/cpp/platform/impl/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
#include "third_party/nearby_connections/cpp/platform/impl/ios/Source/Platform/utils.h"
#import "third_party/objective_c/google_toolbox_for_mac/Foundation/GTMLogger.h"
namespace location {
namespace nearby {
namespace ios {
/** WifiLanInputStream implementation. */
WifiLanInputStream::WifiLanInputStream()
: newDataPackets_([NSMutableArray array]),
accumulatedData_([NSMutableData data]),
condition_([[NSCondition alloc] init]) {
// Create the handlers of incoming data from the remote endpoint.
connectionHandlers_ = [GNCMConnectionHandlers
payloadHandler:^(NSData* data) {
[condition_ lock];
// Add the incoming data to the data packet array to be processed in Read() below.
[newDataPackets_ addObject:data];
[condition_ signal];
[condition_ unlock];
}
disconnectedHandler:^{
[condition_ lock];
// Release the data packet array, meaning the stream has been closed or severed.
newDataPackets_ = nil;
[condition_ signal];
[condition_ unlock];
}];
}
WifiLanInputStream::~WifiLanInputStream() {
NSCAssert(!newDataPackets_, @"WifiLanInputStream not closed before destruction");
}
ExceptionOr<ByteArray> WifiLanInputStream::Read(std::int64_t size) {
// Block until either (a) the connection has been closed, or (b) enough data to return.
NSData* dataToReturn;
[condition_ lock];
while (true) {
// Check if the stream has been closed or severed.
if (!newDataPackets_) break;
if (newDataPackets_.count > 0) {
// Add the packet data to the accumulated data.
for (NSData* data in newDataPackets_) {
if (data.length > 0) {
[accumulatedData_ appendData:data];
}
}
[newDataPackets_ removeAllObjects];
}
if ((size == -1) && (accumulatedData_.length > 0)) {
// Return all of the data.
dataToReturn = accumulatedData_;
accumulatedData_ = [NSMutableData data];
break;
} else if (accumulatedData_.length > 0) {
// Return up to |size| bytes of the data.
std::int64_t sizeToReturn = accumulatedData_.length < size ? accumulatedData_.length : size;
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
dataToReturn = [accumulatedData_ subdataWithRange:range];
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
break;
}
[condition_ wait];
}
[condition_ unlock];
if (dataToReturn) {
GTMLoggerInfo(@"[NEARBY] Input stream: Received data of size: %lu",
(unsigned long)dataToReturn.length);
return ExceptionOr<ByteArray>(ByteArrayFromNSData(dataToReturn));
} else {
return ExceptionOr<ByteArray>{Exception::kIo};
}
}
Exception WifiLanInputStream::Close() {
// Unblock pending read operation.
[condition_ lock];
newDataPackets_ = nil;
[condition_ signal];
[condition_ unlock];
return {Exception::kSuccess};
}
/** WifiLanOutputStream implementation. */
WifiLanOutputStream::~WifiLanOutputStream() {
NSCAssert(!connection_, @"WifiLanOutputStream not closed before destruction");
}
Exception WifiLanOutputStream::Write(const ByteArray& data) {
[condition_ lock];
GTMLoggerDebug(@"[NEARBY] Sending data of size: %lu",
(unsigned long)NSDataFromByteArray(data).length);
NSMutableData* packet = [NSMutableData dataWithData:NSDataFromByteArray(data)];
// Send the data, blocking until the completion handler is called.
__block GNCMPayloadResult sendResult = GNCMPayloadResultFailure;
__block bool isComplete = NO;
NSCondition* condition = condition_; // don't capture |this| in completion
// Check if connection_ is nil, then just don't wait and return as failure.
if (connection_ != nil) {
[connection_ sendData:packet
progressHandler:^(size_t count) {
}
completion:^(GNCMPayloadResult result) {
// Make sure we haven't already reported completion before. This prevents a crash
// where we try leaving a dispatch group more times than we entered it.
// b/79095653.
if (isComplete) {
return;
}
isComplete = YES;
sendResult = result;
[condition lock];
[condition signal];
[condition unlock];
}];
[condition_ wait];
[condition_ unlock];
} else {
sendResult = GNCMPayloadResultFailure;
[condition_ unlock];
}
if (sendResult == GNCMPayloadResultSuccess) {
return {Exception::kSuccess};
} else {
return {Exception::kIo};
}
}
Exception WifiLanOutputStream::Flush() {
// The Write() function block until the data is received by the remote endpoint, so there's
// nothing to do here.
return {Exception::kSuccess};
}
Exception WifiLanOutputStream::Close() {
// Unblock pending write operation.
[condition_ lock];
connection_ = nil;
[condition_ signal];
[condition_ unlock];
return {Exception::kSuccess};
}
/** WifiLanSocket implementation. */
WifiLanSocket::WifiLanSocket(id<GNCMConnection> connection)
: input_stream_(new WifiLanInputStream()),
output_stream_(new WifiLanOutputStream(connection)) {}
WifiLanSocket::~WifiLanSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
bool WifiLanSocket::IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
Exception WifiLanSocket::Close() {
absl::MutexLock lock(&mutex_);
DoClose();
return {Exception::kSuccess};
}
void WifiLanSocket::DoClose() {
if (!closed_) {
input_stream_->Close();
output_stream_->Close();
closed_ = true;
}
}
/** WifiLanServerSocket implementation. */
std::string WifiLanServerSocket::GetName(const std::string& ip_address, int port) {
std::string dot_delimited_string;
if (!ip_address.empty()) {
for (auto byte : ip_address) {
if (!dot_delimited_string.empty()) absl::StrAppend(&dot_delimited_string, ".");
absl::StrAppend(&dot_delimited_string, absl::StrFormat("%d", byte));
}
}
std::string out = absl::StrCat(dot_delimited_string, ":", port);
return out;
}
WifiLanServerSocket::~WifiLanServerSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
while (!closed_ && pending_sockets_.empty()) {
cond_.Wait(&mutex_);
}
// Return early if closed.
if (closed_) return {};
auto remote_socket = std::move(pending_sockets_.extract(pending_sockets_.begin()).value());
return std::move(remote_socket);
}
bool WifiLanServerSocket::Connect(std::unique_ptr<WifiLanSocket> socket) {
absl::MutexLock lock(&mutex_);
if (closed_) {
return false;
}
// add client socket to the pending list
pending_sockets_.insert(std::move(socket));
cond_.SignalAll();
if (closed_) {
return false;
}
return true;
}
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
absl::MutexLock lock(&mutex_);
close_notifier_ = std::move(notifier);
}
Exception WifiLanServerSocket::Close() {
absl::MutexLock lock(&mutex_);
return DoClose();
}
Exception WifiLanServerSocket::DoClose() {
bool should_notify = !closed_;
closed_ = true;
if (should_notify) {
cond_.SignalAll();
if (close_notifier_) {
auto notifier = std::move(close_notifier_);
mutex_.Unlock();
// Notifier may contain calls to public API, and may cause deadlock, if
// mutex_ is held during the call.
notifier();
mutex_.Lock();
}
}
return {Exception::kSuccess};
}
/** WifiLanMedium implementation. */
WifiLanMedium::~WifiLanMedium() {
advertising_info_.Clear();
discovering_info_.Clear();
}
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
std::string service_type = nsd_service_info.GetServiceType();
NSString* serviceType = ObjCStringFromCppString(service_type);
{
absl::MutexLock lock(&mutex_);
if (advertising_info_.Existed(service_type)) {
GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start advertising because "
@"service_type=%@, has started already",
serviceType);
return false;
}
}
// Retrieve service name.
NSString* serviceName = ObjCStringFromCppString(nsd_service_info.GetServiceName());
// Retrieve TXTRecord and convert it to NSDictionary type.
NSDictionary<NSString*, NSData*>* TXTRecordData =
NSDictionaryFromCppTxtRecords(nsd_service_info.GetTxtRecords());
// Get ip address and port to retrieve the server_socket, if not, then return nil.
std::string ip_address = nsd_service_info.GetIPAddress();
int port = nsd_service_info.GetPort();
__block std::string socket_name = WifiLanServerSocket::GetName(ip_address, port);
GNCMBonjourService* service = [[GNCMBonjourService alloc]
initWithServiceName:serviceName
serviceType:serviceType
port:requesting_port_
TXTRecordData:TXTRecordData
endpointConnectedHandler:^GNCMConnectionHandlers*(id<GNCMConnection> connection) {
auto item = server_sockets_.find(socket_name);
WifiLanServerSocket* server_socket = item != server_sockets_.end() ? item->second : nullptr;
if (!server_socket) {
return nil;
}
auto socket = absl::make_unique<WifiLanSocket>(connection);
GNCMConnectionHandlers* connectionHandlers =
static_cast<WifiLanInputStream&>(socket->GetInputStream()).GetConnectionHandlers();
server_socket->Connect(std::move(socket));
return connectionHandlers;
}];
{
absl::MutexLock lock(&mutex_);
advertising_info_.Add(service_type, service);
}
return true;
}
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) {
std::string service_type = nsd_service_info.GetServiceType();
{
absl::MutexLock lock(&mutex_);
if (!advertising_info_.Existed(service_type)) {
GTMLoggerInfo(@"[NEARBY] WifiLan StopAdvertising: Can't stop advertising because we never "
@"started advertising for service_type=%@",
ObjCStringFromCppString(service_type));
return false;
}
advertising_info_.Remove(service_type);
}
return true;
}
bool WifiLanMedium::StartDiscovery(const std::string& service_type,
DiscoveredServiceCallback callback) {
NSString* serviceType = ObjCStringFromCppString(service_type);
{
absl::MutexLock lock(&mutex_);
if (discovering_info_.Existed(service_type)) {
GTMLoggerInfo(@"[NEARBY] WifiLan StartAdvertising: Can't start discovery because "
@"service_type=%@, has started already",
serviceType);
return false;
}
}
GNCMBonjourBrowser* browser = [[GNCMBonjourBrowser alloc]
initWithServiceType:serviceType
endpointFoundHandler:^GNCMEndpointLostHandler(
NSString* endpointId, NSString* serviceType, NSString* serviceName,
NSDictionary<NSString*, NSData*>* _Nullable txtRecordData,
GNCMConnectionRequester requestConnection) {
__block NsdServiceInfo nsd_service_info = {};
nsd_service_info.SetServiceName(CppStringFromObjCString(serviceName));
nsd_service_info.SetServiceType(CppStringFromObjCString(serviceType));
// Set TXTRecord converted from NSDictionary to hash map.
if (txtRecordData != nil) {
auto txt_records = AbslHashMapFromObjCTxtRecords(txtRecordData);
nsd_service_info.SetTxtRecords(txt_records);
}
connection_requesters_.insert({CppStringFromObjCString(serviceType), requestConnection});
callback.service_discovered_cb(nsd_service_info);
return ^{
callback.service_lost_cb(nsd_service_info);
};
}];
{
absl::MutexLock lock(&mutex_);
discovering_info_.Add(service_type, browser);
}
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string& service_type) {
{
absl::MutexLock lock(&mutex_);
if (!discovering_info_.Existed(service_type)) {
GTMLoggerInfo(@"[NEARBY] WifiLan StopDiscovery: Can't stop discovering because "
"we never started discovering.");
return false;
}
discovering_info_.Remove(service_type);
}
return true;
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const NsdServiceInfo& remote_service_info, CancellationFlag* cancellation_flag) {
std::string service_type = remote_service_info.GetServiceType();
GTMLoggerInfo(@"[NEARBY] WifiLan ConnectToService, service_type=%@",
ObjCStringFromCppString(service_type));
GNCMConnectionRequester connection_requester = nil;
{
absl::MutexLock lock(&mutex_);
const auto& it = connection_requesters_.find(service_type);
if (it == connection_requesters_.end()) {
return {};
}
connection_requester = it->second;
}
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block std::unique_ptr<WifiLanSocket> socket;
if (connection_requester != nil) {
if (cancellation_flag->Cancelled()) {
GTMLoggerError(@"[NEARBY] WifiLan Connect: Has been cancelled: service_type=%@",
ObjCStringFromCppString(service_type));
dispatch_group_leave(group); // unblock
return {};
}
connection_requester(^(id<GNCMConnection> connection) {
// If the connection wasn't successfully established, return a NULL socket.
if (connection) {
socket = absl::make_unique<WifiLanSocket>(connection);
}
dispatch_group_leave(group); // unblock
return socket != nullptr ? static_cast<WifiLanInputStream&>(socket->GetInputStream())
.GetConnectionHandlers()
: nullptr;
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return std::move(socket);
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const std::string& ip_address, int port, CancellationFlag* cancellation_flag) {
// Not implemented.
return {};
}
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(int port) {
auto server_socket = std::make_unique<WifiLanServerSocket>();
// The fake ip address and port need to be set here since they can't be retrieved before
// StartAadvertising begins. Furthermore, NSNetService can't resolve ip address when finding
// service. Try to fake them and make it socket name as a key of server_sockets_ which acts
// the same socket binding.
server_socket->SetIPAddress(GetFakeIPAddress());
requesting_port_ = port;
server_socket->SetPort(requesting_port_ == 0 ? GetFakePort() : requesting_port_);
std::string socket_name =
WifiLanServerSocket::GetName(server_socket->GetIPAddress(), server_socket->GetPort());
server_socket->SetCloseNotifier([this, socket_name]() {
absl::MutexLock lock(&mutex_);
server_sockets_.erase(socket_name);
});
GTMLoggerInfo(@"[NEARBY] WifiLan Adding server socket, socket_name=%@",
ObjCStringFromCppString(socket_name));
absl::MutexLock lock(&mutex_);
server_sockets_.insert({socket_name, server_socket.get()});
return server_socket;
}
std::string WifiLanMedium::GetFakeIPAddress() const {
std::string ip_address;
ip_address.resize(4);
uint32_t raw_ip_addr = Prng().NextUint32();
ip_address[0] = static_cast<char>(raw_ip_addr >> 24);
ip_address[1] = static_cast<char>(raw_ip_addr >> 16);
ip_address[2] = static_cast<char>(raw_ip_addr >> 8);
ip_address[3] = static_cast<char>(raw_ip_addr >> 0);
return ip_address;
}
int WifiLanMedium::GetFakePort() const {
uint16_t port = Prng().NextUint32();
return port;
}
} // namespace ios
} // namespace nearby
} // namespace location