diff --git a/internal/platform/implementation/device_info.h b/internal/platform/implementation/device_info.h index 7247fa85..da35de37 100644 --- a/internal/platform/implementation/device_info.h +++ b/internal/platform/implementation/device_info.h @@ -28,8 +28,8 @@ namespace api { class DeviceInfo { public: enum class ScreenStatus { kUndetermined = 0, kLocked, kUnlocked }; - enum class DeviceType { kUnknown = 0, kPhone, kTablet, kLaptop }; - enum class OsType { kUnknown = 0, kAndroid, kChromeOs, kIos, kWindows }; + enum class DeviceType { kUnknown = 0, kPhone, kTablet, kLaptop, kDesktop }; + enum class OsType { kUnknown = 0, kAndroid, kChromeOs, kIos, kWindows, kLinux }; virtual ~DeviceInfo() = default; diff --git a/internal/platform/implementation/linux/BUILD b/internal/platform/implementation/linux/BUILD new file mode 100644 index 00000000..801ab31f --- /dev/null +++ b/internal/platform/implementation/linux/BUILD @@ -0,0 +1,290 @@ +# 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"]) + +cc_library( + name = "types", + srcs = [ + "device_info.cc", + "log_message.cc", + "timer.cc", + ], + hdrs = [ + "atomic_boolean.h", + "atomic_reference.h", + "bluetooth_adapter.h", + "condition_variable.h", + "device_info.h", + "executor.h", + "future.h", + "input_file.h", + "listenable_future.h", + "log_message.h", + "mutex.h", + "output_file.h", + "preferences_manager.h", + "scheduled_executor.h", + "settable_future.h", + "submittable_executor.h", + "timer.h", + "utils.h", + ], + defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"], + visibility = ["//visibility:public"], + deps = [ + ":comm", + "//base", + "//base:stringprintf", + "//internal/base:bluetooth_address", + "//internal/platform:base", + "//internal/platform:logging", + "//internal/platform:types", + "//internal/platform:uuid", + "//internal/platform/implementation:types", + "//strings:strappendv", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + "@nlohmann_json//:json", + ], +) + +cc_library( + name = "comm", + hdrs = [ + "ble.h", + "ble_gatt_client.h", + "ble_gatt_server.h", + "ble_medium.h", + "ble_peripheral.h", + "ble_socket.h", + "ble_v2.h", + "ble_v2_peripheral.h", + "ble_v2_server_socket.h", + "ble_v2_socket.h", + "bluetooth_adapter.h", + "bluetooth_classic.h", + "bluetooth_classic_device.h", + "bluetooth_classic_medium.h", + "bluetooth_classic_server_socket.h", + "bluetooth_classic_socket.h", + "bluetooth_pairing.h", + "condition_variable.h", + "executor.h", + "file.h", + "file_path.h", + "http_loader.h", + "mutex.h", + "scheduled_executor.h", + "server_sync.h", + "submittable_executor.h", + "thread_pool.h", + "webrtc.h", + "wifi.h", + "wifi_direct.h", + "wifi_hotspot.h", + "wifi_lan.h", + ], + visibility = ["//visibility:private"], + deps = [ + "//internal/platform:base", + "//internal/platform:comm", + "//internal/platform:types", + "//internal/platform:uuid", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:types", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "crypto", + srcs = [ + "crypto.cc", + ], + visibility = ["//visibility:private"], + deps = [ + "//internal/platform:base", + "//internal/platform/implementation:types", + "@boringssl//:crypto", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "linux", + srcs = [ + "ble_gatt_client.cc", + "ble_gatt_server.cc", + "ble_medium.cc", + "ble_socket.cc", + "ble_v2.cc", + "ble_v2_peripheral.cc", + "ble_v2_server_socket.cc", + "ble_v2_socket.cc", + "bluetooth_adapter.cc", + "bluetooth_classic_device.cc", + "bluetooth_classic_medium.cc", + "bluetooth_classic_server_socket.cc", + "bluetooth_classic_socket.cc", + "bluetooth_pairing.cc", + "executor.cc", + "file.cc", + "file_path.cc", + "http_loader.cc", + "platform.cc", + "preferences_manager.cc", + "preferences_repository.cc", + "preferences_repository.h", + "scheduled_executor.cc", + "submittable_executor.cc", + "system_clock.cc", + "thread_pool.cc", + "utils.cc", + "webrtc.cc", + "wifi_direct_medium.cc", + "wifi_direct_server_socket.cc", + "wifi_direct_socket.cc", + "wifi_hotspot_medium.cc", + "wifi_hotspot_server_socket.cc", + "wifi_hotspot_socket.cc", + "wifi_lan_medium.cc", + "wifi_lan_server_socket.cc", + "wifi_lan_socket.cc", + "wifi_medium.cc", + ], + defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"], + visibility = [ + "//connections:__subpackages__", + "//fastpair:__subpackages__", + "//location/nearby:__subpackages__", + "//presence:__subpackages__", + ], + deps = [ + ":comm", + ":crypto", # build_cleaner: keep + ":types", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "//internal/platform:comm", + "//internal/platform:logging", + "//internal/platform:types", + "//internal/platform:uuid", + "//internal/platform/flags:platform_flags", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", + "//internal/platform/implementation/shared:count_down_latch", + "//internal/platform/implementation/shared:file", + "//third_party/webrtc/files/stable/webrtc/api/task_queue:default_task_queue_factory", + "//third_party/webrtc/files/stable/webrtc/rtc_base:checks", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", + "@nlohmann_json//:json", + ], +) + +cc_library( + name = "test_utils", + srcs = [ + "test_utils.cc", + ], + hdrs = [ + "test_data.h", + "test_utils.h", + ], + visibility = [ + "//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private. + ], + deps = [ + "//internal/platform:base", + "@nlohmann_json//:json", + ], +) + +cc_test( + name = "impl_test", + size = "small", + srcs = [ + "atomic_boolean_test.cc", + "atomic_reference_test.cc", + "ble_gatt_server_test.cc", + "ble_medium_test.cc", + "ble_v2_peripheral_test.cc", + "ble_v2_test.cc", + "bluetooth_adapter_test.cc", + "count_down_latch_test.cc", + "crypto_test.cc", + "device_info_test.cc", + "executor_test.cc", + "file_path_test.cc", + "http_loader_test.cc", + "preferences_manager_test.cc", + "preferences_repository_test.cc", + "scheduled_executor_test.cc", + "submittable_executor_test.cc", + "thread_pool_test.cc", + "timer_test.cc", + "utils_test.cc", + "webrtc_test.cc", + ], + tags = ["notap"], + deps = [ + ":comm", + ":crypto", + ":test_utils", + ":types", + ":windows", + "//internal/platform:base", + "//internal/platform:logging", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", + "//internal/platform/implementation/shared:count_down_latch", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + "@com_google_googletest//:gtest_main", + "@nlohmann_json//:json", + ], +) diff --git a/internal/platform/implementation/linux/atomic_boolean.h b/internal/platform/implementation/linux/atomic_boolean.h new file mode 100644 index 00000000..8147dabb --- /dev/null +++ b/internal/platform/implementation/linux/atomic_boolean.h @@ -0,0 +1,43 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_ +#define PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_ + +#include + +#include "internal/platform/implementation/atomic_boolean.h" + +namespace nearby { +namespace linux { + +// A boolean value that may be updated atomically. +class AtomicBoolean : public api::AtomicBoolean { + public: + ~AtomicBoolean() override = default; + + // Atomically read and return current value. + bool Get() const override { return atomic_boolean_; }; + + // Atomically exchange original value with a new one. Return previous value. + bool Set(bool value) override { return atomic_boolean_.exchange(value); }; + + private: + std::atomic_bool atomic_boolean_ = false; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_ \ No newline at end of file diff --git a/internal/platform/implementation/linux/atomic_boolean_test.cc b/internal/platform/implementation/linux/atomic_boolean_test.cc new file mode 100644 index 00000000..ecd5e41a --- /dev/null +++ b/internal/platform/implementation/linux/atomic_boolean_test.cc @@ -0,0 +1,32 @@ +// 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/linux/atomic_boolean.h" + +#include "gtest/gtest.h" + +TEST(atomic_boolean, SuccessfulCreation) { + // Arrange + nearby::linux::AtomicBoolean atomicBoolean; + bool oldValue = true; + bool result = false; + + // Act + oldValue = atomicBoolean.Set(true); + result = atomicBoolean.Get(); + + // Assert + EXPECT_TRUE(result); + EXPECT_FALSE(oldValue); +} \ No newline at end of file diff --git a/internal/platform/implementation/linux/atomic_reference.h b/internal/platform/implementation/linux/atomic_reference.h new file mode 100644 index 00000000..ec7793dd --- /dev/null +++ b/internal/platform/implementation/linux/atomic_reference.h @@ -0,0 +1,43 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_ +#define PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_ + +#include + +#include "internal/platform/implementation/atomic_reference.h" + +namespace nearby { +namespace linux { + +// Type that allows 32-bit atomic reads and writes. +class AtomicUint32 : public api::AtomicUint32 { + public: + ~AtomicUint32() override = default; + + // Atomically reads and returns stored value. + std::uint32_t Get() const override { return atomic_uint32_; }; + + // Atomically stores value. + void Set(std::uint32_t value) override { atomic_uint32_ = value; } + + private: + std::atomic_int32_t atomic_uint32_ = 0; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_ \ No newline at end of file diff --git a/internal/platform/implementation/linux/atomic_reference_test.cc b/internal/platform/implementation/linux/atomic_reference_test.cc new file mode 100644 index 00000000..8e69a64a --- /dev/null +++ b/internal/platform/implementation/linux/atomic_reference_test.cc @@ -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. + +#include "internal/platform/implementation/linux/atomic_reference.h" + +#include "gtest/gtest.h" + +TEST(atomic_reference, SuccessfulCreation) { + // Arrange + nearby::linux::AtomicUint32 atomicUint32; + uint32_t result = UINT32_MAX; + const uint32_t expected = 0; + + // Act + result = atomicUint32.Get(); + + // Assert + EXPECT_EQ(result, expected); +} + +TEST(atomic_reference, SuccessfulMaxSet) { + // Arrange + nearby::linux::AtomicUint32 atomicUint32; + uint32_t result = 0; + const uint32_t expected = UINT32_MAX; + + // Act + atomicUint32.Set(UINT32_MAX); + result = atomicUint32.Get(); + + // Assert + EXPECT_EQ(result, expected); +} + +TEST(atomic_reference, SuccessfulMinSet) { + // Arrange + nearby::linux::AtomicUint32 atomicUint32; + uint32_t result = UINT32_MAX; + const uint32_t expected = 0; + + // Act + atomicUint32.Set(0); + result = atomicUint32.Get(); + + // Assert + EXPECT_EQ(result, expected); +} + +TEST(atomic_reference, SetNegativeOneReturnsMAXUINT) { + // Arrange + nearby::linux::AtomicUint32 atomicUint32; + uint32_t result = 0; + const uint32_t expected = UINT32_MAX; + + // Act + atomicUint32.Set(-1); // Try Set -1, should actually store UINT32_MAX + result = atomicUint32.Get(); + + // Assert + EXPECT_EQ(result, expected); +} \ No newline at end of file diff --git a/internal/platform/implementation/linux/condition_variable.h b/internal/platform/implementation/linux/condition_variable.h new file mode 100644 index 00000000..3661b3a7 --- /dev/null +++ b/internal/platform/implementation/linux/condition_variable.h @@ -0,0 +1,51 @@ +// 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. +#ifndef PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_ +#define PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_ + +#include // NOLINT + +#include "internal/platform/implementation/condition_variable.h" +#include "internal/platform/implementation/linux/mutex.h" +#include "internal/platform/mutex.h" + +namespace nearby { +namespace linux { + +class ConditionVariable : public api::ConditionVariable { + public: + explicit ConditionVariable(api::Mutex* mutex) + : mutex_(&(static_cast(mutex))->mutex_) {} + ~ConditionVariable() override = default; + + Exception Wait() override { + cond_var_.Wait(mutex_); + return {Exception::kSuccess}; + } + + Exception Wait(absl::Duration timeout) override { + cond_var_.WaitWithTimeout(mutex_, timeout); + return {Exception::kSuccess}; + } + + void Notify() override { cond_var_.SignalAll(); } + + private: + absl::Mutex* mutex_; + absl::CondVar cond_var_; +}; +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_ diff --git a/internal/platform/implementation/linux/condition_variable_test.cc b/internal/platform/implementation/linux/condition_variable_test.cc new file mode 100644 index 00000000..a45aae8e --- /dev/null +++ b/internal/platform/implementation/linux/condition_variable_test.cc @@ -0,0 +1,102 @@ +// 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. + +#include "internal/platform/implementation/linux/condition_variable.h" + +#include // NOLINT + +#include "absl/time/clock.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/linux/mutex.h" + +#include "gtest/gtest.h" + +class ConditionVariableTests : public testing::Test { + public: + class ConditionVariableTest { + public: + ConditionVariableTest() {} + + std::future WaitForEvent(bool timedWait, // NOLINT + const absl::Duration* timeout) { + return std::async( + std::launch::async, + [this, timedWait, timeout]() mutable -> bool { + + if (timedWait == true) { + auto result = this->condition_variable_actual_.Wait(*timeout); + if (result.value == nearby::Exception::kSuccess) { + return true; + } else { + return false; + } + } else { + this->condition_variable_actual_.Wait(); + } + return true; + }); + } + + void PostEvent() { + absl::MutexLock(&mutex_actual_.GetMutex()); + condition_variable_actual_.Notify(); + } + + private: + nearby::linux::Mutex mutex_actual_ = + nearby::linux::Mutex(nearby::linux::Mutex::Mode::kRegular); + nearby::linux::Mutex& mutex_ = mutex_actual_; + nearby::linux::ConditionVariable condition_variable_actual_ = + nearby::linux::ConditionVariable(&mutex_); + nearby::linux::ConditionVariable& condition_variable_ = + condition_variable_actual_; + }; +}; + +TEST_F(ConditionVariableTests, SuccessfulCreation) { + // Arrange + ConditionVariableTest conditionVariableTest; + + auto result = conditionVariableTest.WaitForEvent(false, nullptr); + + sleep(1); + + // Act + conditionVariableTest.PostEvent(); + + // Assert + ASSERT_TRUE(result.get()); +} + +TEST_F(ConditionVariableTests, TimedCreation) { + // Arrange + ConditionVariableTest conditionVariableTest; + const absl::Duration duration = absl::Milliseconds(100); + + // Act + auto result = conditionVariableTest.WaitForEvent(true, &duration); + + // Assert + ASSERT_FALSE(result.get()); // Timed out + + // Act + result = conditionVariableTest.WaitForEvent(true, &duration); + + sleep(1); + + conditionVariableTest.PostEvent(); + + // Assert + ASSERT_TRUE(result.get()); // Didn't timeout +} diff --git a/internal/platform/implementation/linux/crypto.cc b/internal/platform/implementation/linux/crypto.cc new file mode 100644 index 00000000..bdd1d926 --- /dev/null +++ b/internal/platform/implementation/linux/crypto.cc @@ -0,0 +1,54 @@ +// 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" + +#include +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" + +#include + +// Function implementations for platform/implementation/crypto.h. + +namespace nearby { + +// Initialize global crypto state. +void Crypto::Init() {} + +static ByteArray Hash(absl::string_view input, const EVP_MD* algo) { + unsigned int md_out_size = EVP_MAX_MD_SIZE; + uint8_t digest_buffer[EVP_MAX_MD_SIZE]; + if (input.empty()) return {}; + + if (!EVP_Digest(input.data(), input.size(), digest_buffer, &md_out_size, algo, + nullptr)) + return {}; + + return ByteArray{reinterpret_cast(digest_buffer), md_out_size}; +} + +// Return MD5 hash of input. +ByteArray Crypto::Md5(absl::string_view input) { + return Hash(input, EVP_md5()); +} + +// Return SHA256 hash of input. +ByteArray Crypto::Sha256(absl::string_view input) { + return Hash(input, EVP_sha256()); +} + +} // namespace nearby diff --git a/internal/platform/implementation/linux/crypto_test.cc b/internal/platform/implementation/linux/crypto_test.cc new file mode 100644 index 00000000..551616ee --- /dev/null +++ b/internal/platform/implementation/linux/crypto_test.cc @@ -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. + +#include "internal/platform/implementation/crypto.h" + +#include + +#include "gtest/gtest.h" + +namespace nearby { +namespace { + +TEST(CryptoTest, Md5Hash) { + const std::string input{"Hello Nearby Connection"}; + const ByteArray expected_md5( + "\x94\xa3\xbe\xc1\x8d\x30\xe3\x24\x5f\xa1\x4c\xee\xe7\x52\xe9\x36"); + ByteArray md5_hash = Crypto::Md5(input); + EXPECT_EQ(md5_hash, expected_md5); +} + +TEST(CryptoTest, Md5HashOnEmptyInput) { + EXPECT_EQ(Crypto::Md5(""), ByteArray{}); +} + +TEST(CryptoTest, Sha256Hash) { + const std::string input("Hello Nearby Connection"); + const ByteArray expected_sha256( + "\xb4\x24\xd3\xc0\x58\x12\x9a\x42\xcb\x81\xa0\x4b\x6e\x9d\xfe\x45\x45\x9f" + "\x15\xf7\xc0\xa9\x32\x2f\xfb\x9\x45\xf0\xf9\xbe\x75\xb"); + ByteArray sha256_hash = Crypto::Sha256(input); + EXPECT_EQ(sha256_hash, expected_sha256); +} + +TEST(CryptoTest, Sha256HashOnEmptyInput) { + EXPECT_EQ(Crypto::Sha256(""), ByteArray{}); +} + +} // namespace +} // namespace nearby \ No newline at end of file diff --git a/internal/platform/implementation/linux/device_info.cc b/internal/platform/implementation/linux/device_info.cc new file mode 100644 index 00000000..b05aeda8 --- /dev/null +++ b/internal/platform/implementation/linux/device_info.cc @@ -0,0 +1,264 @@ +// 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. + +#include "internal/platform/implementation/linux/device_info.h" + +// For Linux device specific stuff +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "internal/base/bluetooth_address.h" +#include "internal/platform/implementation/device_info.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace linux { + +std::optional DeviceInfo::GetOsDeviceName() const { + // As I know of, there is no way to fully determine the length of the Hostname (device nickname) + + // https://stackoverflow.com/a/18851841 Max Host name length is limited to 64 bytes + char *device_name = new char[HOST_NAME_MAX]; + + // Linux hostnames are limited to UTF-8, 8-bit wide characters, so we need to convert + // that to the return type for a UTF-16 string, 16-bits wide. + if (gethostname(device_name, HOST_NAME_MAX) == 0) { + std::string name(device_name); + delete[] device_name; + return std::wstring_convert, char16_t>().from_bytes(name); + } + + NEARBY_LOGS(ERROR) << ": Failed to get device name, error:" + << strerror(errno); + delete[] device_name; + return std::nullopt; +} + +api::DeviceInfo::DeviceType DeviceInfo::GetDeviceType() const { + // While there is no *official* way to detect if a Linux system is a laptop or not, we can try to + // find its chassis type and make a very educated guess as to what the user is using. + // See https://superuser.com/a/1107191 + std::fstream chassis_type("/sys/class/dmi/id/chassis_type", std::ios::binary | std::ios::in); + + char chartype = 0; + + chassis_type.get(chartype); + + // The code is stored as text in the file, in order to get a correct + // representation in decimal, we can subtract 48 from it. + int type = chartype - 48; + + switch (type) { + case 3: + case 4: + // Type 3 and 4 are both types of desktops + return api::DeviceInfo::DeviceType::kDesktop; + case 6: + case 7: + // Type 6 and 7 are both Towers + return api::DeviceInfo::DeviceType::kDesktop; + case 9: + case 10: + // Type 9 and 10 are laptop esc, laptop and notebook + return api::DeviceInfo::DeviceType::kLaptop; + case 11: + case 30: + // Type 11 is labled as "Hand Held" + return api::DeviceInfo::DeviceType::kTablet; + case 31: + case 32: + // Type 31 and 32 are Convertable / Detatchable + return api::DeviceInfo::DeviceType::kLaptop; + default: + return api::DeviceInfo::DeviceType::kUnknown; + } +} + +api::DeviceInfo::OsType DeviceInfo::GetOsType() const { + return api::DeviceInfo::OsType::kLinux; +} + +std::optional DeviceInfo::GetFullName() const { + // We can use the C function getpwnam() to get information in a passwd database. + // The users full name is optionally in the passwd->pw_gecos member of the passwd struct. + + struct passwd *full_user_data = getpwnam(getlogin()); + std::u16string u16str; + + if (full_user_data == nullptr) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error retrieving locally authenticated user."; + return std::nullopt; + } + + // The GECOS field is optional and used for information purposes only. + // Usually it would contain the full name of the user. + // See https://man7.org/linux/man-pages/man5/passwd.5.html + u16str = *full_user_data->pw_gecos; + + if (u16str.empty()) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error retrieving full name of user. (GECOS field empty)"; + return std::nullopt; + } + + return u16str; +} + +std::optional DeviceInfo::GetGivenName() const { + std::optional user_full_name = GetFullName(); + + if (user_full_name == std::nullopt) { + NEARBY_LOGS(ERROR) << __func__ << ": Error retrieving first name of user."; + return std::nullopt; + } + + if (user_full_name->empty()) { + NEARBY_LOGS(ERROR) + << __func__ << ": Error unboxing string value for first name of user."; + return std::nullopt; + } + + std::u16string::size_type seperator = user_full_name->find_first_of(u" "); + + // If the Full Name doesn't contain a space, then we assume the whole thing is a first name + if (seperator == std::u16string::npos) { + return user_full_name.value(); + } + + return user_full_name->substr(*user_full_name->begin(), seperator); +} + +std::optional DeviceInfo::GetLastName() const { + std::optional user_full_name = GetFullName(); + + if (user_full_name == std::nullopt) { + NEARBY_LOGS(ERROR) << __func__ << ": Error retrieving last name of user."; + return std::nullopt; + } + + if (user_full_name->empty()) { + NEARBY_LOGS(ERROR) + << __func__ << ": Error unboxing string value for last name of user."; + return std::nullopt; + } + std::u16string::size_type seperator = user_full_name->find_first_of(u" "); + + // If the Full Name doesn't contain a space, then we assume there is no last name + if (seperator == std::u16string::npos) { + return std::nullopt; + } + + return user_full_name->substr(*user_full_name->begin(), seperator); +} + +std::optional DeviceInfo::GetProfileUserName() const { + + std::string user_name = getlogin(); + + + if (user_name.empty()) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error retrieving account name of user." + << strerror(errno); + return std::nullopt; + } + + return user_name; +} + +std::optional DeviceInfo::GetDownloadPath() const { + // This assumes xdg-user-dir is installed on the target system, which is better + // than guessing completely. This is 99.9% of the time going to be installed + // automatically by the Distro/DE + FILE *fp = popen("xdg-user-dir DOWNLOAD", "r"); + char *path = new char[512]; + if (fp == nullptr) { + pclose(fp); + delete fp; + delete[] path; + return std::nullopt; + } + + while (fgets(path, sizeof(path), fp) != nullptr) { + + } + int exit_status = WEXITSTATUS(fclose(fp)); + if (exit_status != 0) { + delete fp; + delete[] path; + return std::nullopt; + } + delete fp; + std::filesystem::path fpath = std::filesystem::path(path); + delete[] path; + return fpath; +} + +std::optional DeviceInfo::GetLocalAppDataPath() const { + //TODO: Figure out how to get cross distro path + + return std::nullopt; +} + +std::optional DeviceInfo::GetCommonAppDataPath() const { + //TODO: Figure out how to get cross distro path + + return std::nullopt; +} + +std::optional DeviceInfo::GetTemporaryPath() const { + return std::filesystem::temp_directory_path(); +} + +std::optional DeviceInfo::GetLogPath() const { + //TODO: Figure out how to get cross distro path + + return std::nullopt; +} + +std::optional DeviceInfo::GetCrashDumpPath() const { + //TODO: Figure out how to get cross distro path + + return std::nullopt; +} + +bool DeviceInfo::IsScreenLocked() const { + // TODO: Determine if it's actually possible to detect Linux screen lock cross DE, WM, WE, etc. + return false; +} + +void DeviceInfo::RegisterScreenLockedListener(absl::string_view listener_name, + std::function callback) { + //TODO: Figure out what this does + // Assuming it has to do with detecting if the screen is locked, that may not be possible. +} + +void DeviceInfo::UnregisterScreenLockedListener(absl::string_view listener_name) { + //TODO: Figure out what this does too + // Assuming it has to do with detecting if the screen is locked, that may not be possible. +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/device_info.h b/internal/platform/implementation/linux/device_info.h new file mode 100644 index 00000000..55e80eda --- /dev/null +++ b/internal/platform/implementation/linux/device_info.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef PLATFORM_IMPL_LINUX_DEVICE_INFO_H_ +#define PLATFORM_IMPL_LINUX_DEVICE_INFO_H_ + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/device_info.h" + +namespace nearby { +namespace linux { + +class DeviceInfo : public api::DeviceInfo { + public: + ~DeviceInfo() override = default; + + std::optional GetOsDeviceName() const override; + api::DeviceInfo::DeviceType GetDeviceType() const override; + api::DeviceInfo::OsType GetOsType() const override; + std::optional GetFullName() const override; + std::optional GetGivenName() const override; + std::optional GetLastName() const override; + std::optional GetProfileUserName() const override; + + std::optional GetDownloadPath() const override; + std::optional GetLocalAppDataPath() const override; + std::optional GetCommonAppDataPath() const override; + std::optional GetTemporaryPath() const override; + std::optional GetLogPath() const override; + std::optional GetCrashDumpPath() const override; + + bool IsScreenLocked() const override; + void RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) override; + void UnregisterScreenLockedListener(absl::string_view listener_name) override; + absl::flat_hash_map> + screen_locked_listeners_; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_DEVICE_INFO_H_ \ No newline at end of file diff --git a/internal/platform/implementation/linux/executor.cc b/internal/platform/implementation/linux/executor.cc new file mode 100644 index 00000000..37dfcd9c --- /dev/null +++ b/internal/platform/implementation/linux/executor.cc @@ -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. + +#include "internal/platform/implementation/linux/executor.h" + +#include + +#include "internal/platform/logging.h" + +namespace nearby { +namespace linux { + +Executor::Executor() : Executor(1) {} + +Executor::Executor(int32_t max_concurrency) + : max_concurrency_(max_concurrency) { + assert(max_concurrency_ >= 1); + thread_pool_ = linux::ThreadPool::Create(max_concurrency); + assert(thread_pool_ != nullptr); +} + +void Executor::Execute(Runnable&& runnable) { + if (shut_down_) { + NEARBY_LOGS(VERBOSE) << "Warning: " << __func__ + << ": Attempt to execute on a shut down pool."; + return; + } + + if (runnable == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Runnable was null."; + return; + } + + thread_pool_->Run(std::move(runnable)); +} + +void Executor::Shutdown() { + shut_down_ = true; + thread_pool_->ShutDown(); + thread_pool_ = nullptr; +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/executor.h b/internal/platform/implementation/linux/executor.h new file mode 100644 index 00000000..bd96ef06 --- /dev/null +++ b/internal/platform/implementation/linux/executor.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. + +#ifndef PLATFORM_IMPL_LINUX_EXECUTOR_H_ +#define PLATFORM_IMPL_LINUX_EXECUTOR_H_ + +#include +#include + +#include "internal/platform/implementation/executor.h" +#include "internal/platform/implementation/linux/thread_pool.h" + +namespace nearby { +namespace linux { + +// This abstract class is the superclass of all classes representing an +// Executor. +class Executor : public api::Executor { + public: + Executor(); + explicit Executor(int max_concurrency); + + // Before returning from destructor, executor must wait for all pending + // jobs to finish. + ~Executor() override = default; + + void Execute(Runnable&& runnable) override; + void Shutdown() override; + + private: + std::unique_ptr thread_pool_ = nullptr; + std::atomic shut_down_ = false; + int32_t max_concurrency_; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_EXECUTOR_H_ diff --git a/internal/platform/implementation/linux/executor_test.cc b/internal/platform/implementation/linux/executor_test.cc new file mode 100644 index 00000000..858c7fc2 --- /dev/null +++ b/internal/platform/implementation/linux/executor_test.cc @@ -0,0 +1,323 @@ +// 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. + +#include "internal/platform/implementation/linux/executor.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "absl/time/time.h" +#include "internal/platform/implementation/linux/test_data.h" + +namespace nearby { +namespace linux { +namespace { + +constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200); + +TEST(ExecutorTests, SingleThreadedExecutorSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto executor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + executor->Execute([&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + + ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + executor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(ExecutorTests, SingleThreadedExecutorAfterShutdownFails) { + // Arrange + std::string expected(""); + + std::unique_ptr executor = std::make_unique(); + std::unique_ptr output = std::make_unique(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + executor->Shutdown(); + + // Act + executor->Execute([&output, &threadIds]() { + threadIds->push_back(std::this_thread::get_id()); + output->append(RUNNABLE_0_TEXT.c_str()); + }); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 1); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(*output.get(), expected); +} + +TEST(ExecutorTests, SingleThreadedExecutorExecuteNullSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto executor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + executor->Execute(nullptr); + executor->Execute([&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + executor->Execute(nullptr); + + ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + executor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(ExecutorTests, SingleThreadedExecutorMultipleTasksSucceeds) { + absl::BlockingCounter block_count(5); + + // Arrange + std::string expected(RUNNABLE_ALL_TEXT.c_str()); + + auto executor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + auto parent_thread = std::this_thread::get_id(); + + // Act + for (int index = 0; index < 5; index++) { + executor->Execute([&, index]() { + threadIds->push_back(std::this_thread::get_id()); + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%s%d, ", RUNNABLE_TEXT.c_str(), index); + output.append(std::string(buffer)); + block_count.DecrementCount(); + }); + } + + block_count.Wait(); + executor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 5); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), parent_thread); + // We should've run all runnables on the worker thread + auto workerThreadId = threadIds->at(0); + for (int index = 0; index < threadIds->size(); index++) { + ASSERT_EQ(threadIds->at(index), workerThreadId); + } + + // We should of run them in the order submitted + ASSERT_EQ(output, expected); +} + +TEST(ExecutorTests, MultiThreadedExecutorSingleTaskSucceeds) { + absl::Notification notification; + + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto executor = std::make_unique(2); + + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + std::shared_ptr output = std::make_shared(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + executor->Execute([&, output]() { + threadIds->push_back(std::this_thread::get_id()); + output->append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + + ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + executor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run the task + ASSERT_EQ(*output.get(), expected); +} + +TEST(ExecutorTests, MultiThreadedExecutorMultipleTasksSucceeds) { + absl::BlockingCounter block_count(5); + + // Arrange + auto executor = std::make_unique(2); + + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + std::shared_ptr output = std::make_shared(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + for (int index = 0; index < 5; index++) { + executor->Execute([&, index]() { + threadIds->push_back(std::this_thread::get_id()); + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%s %d, ", RUNNABLE_TEXT.c_str(), index); + output->append(std::string(buffer)); + block_count.DecrementCount(); + }); + } + + block_count.Wait(); + executor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 6); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); +} + +TEST(ExecutorTests, MultiThreadedExecutorSingleTaskAfterShutdownFails) { + // Arrange + std::string expected(""); + + auto executor = std::make_unique(2); + + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + std::shared_ptr output = std::make_shared(); + + threadIds->push_back(std::this_thread::get_id()); + + executor->Shutdown(); + + // Act + executor->Execute([output, &threadIds]() { + threadIds->push_back(std::this_thread::get_id()); + output->append(RUNNABLE_0_TEXT.c_str()); + }); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 1); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run the task + ASSERT_EQ(*output.get(), expected); +} + +TEST(ExecutorTests, + MultiThreadedExecutorMultipleTasksLargeNumberOfThreadsSucceeds) { + absl::BlockingCounter block_count(250); + + // Arrange + auto executor = std::make_unique(32); + + // Container to note threads that ran + std::vector threadIds = std::vector(); + + threadIds.push_back(std::this_thread::get_id()); + absl::Mutex mutex; + // Act + for (int index = 0; index < 250; index++) { + executor->Execute([&]() mutable { + std::thread::id id = std::this_thread::get_id(); + { + absl::MutexLock lock(&mutex); + threadIds.push_back(id); + } + + // Using rand since this is in a critical section + // and windows doesn't have a rand_r anyway + auto sleepTime = (std::rand() % 101) + 1; // NOLINT + + sleep(sleepTime); + block_count.DecrementCount(); + }); + } + + block_count.Wait(); + executor->Shutdown(); + + // Assert + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds.at(0)); + + // We should've run 1 time on the main thread, and 200 times on the + // workerThreads + ASSERT_EQ(threadIds.size(), 251); +} + +} // namespace +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/file.cc b/internal/platform/implementation/linux/file.cc new file mode 100644 index 00000000..acb5fd0c --- /dev/null +++ b/internal/platform/implementation/linux/file.cc @@ -0,0 +1,111 @@ +// 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/linux/file.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/linux/utils.h" + +namespace nearby { +namespace linux { + +// InputFile +std::unique_ptr IOFile::CreateInputFile( + const absl::string_view file_path, size_t size) { + return absl::WrapUnique(new IOFile(file_path, size)); +} + +IOFile::IOFile(const absl::string_view file_path, size_t size) + : path_(file_path) { + // Always open input file path as wide string on Linux platform. + file_.open(std::filesystem::path(linux::string_to_wstring(path_)), std::ios::binary | std::ios::in | std::ios::ate); + + total_size_ = file_.tellg(); + file_.seekg(0); +} + +std::unique_ptr IOFile::CreateOutputFile(const absl::string_view path) { + return std::unique_ptr(new IOFile(path)); +} + +IOFile::IOFile(const absl::string_view file_path) + : file_(), path_(file_path), total_size_(0) { + // Always open input file path as wide string on Windows platform. + std::wstring_convert> converter; + file_.open(std::filesystem::path(converter.from_bytes(path_)), std::ios::binary | std::ios::out); +} + +ExceptionOr IOFile::Read(std::int64_t size) { + if (!file_.is_open()) { + return ExceptionOr{Exception::kIo}; + } + + if (file_.peek() == EOF) { + return ExceptionOr{ByteArray{}}; + } + + if (!file_.good()) { + return ExceptionOr{Exception::kIo}; + } + + ByteArray bytes(size); + std::unique_ptr read_bytes{new char[size]}; + file_.read(read_bytes.get(), static_cast(size)); + auto num_bytes_read = file_.gcount(); + if (num_bytes_read == 0) { + return ExceptionOr{Exception::kIo}; + } + + return ExceptionOr(ByteArray(read_bytes.get(), num_bytes_read)); +} + +Exception IOFile::Close() { + if (file_.is_open()) { + file_.close(); + } + return {Exception::kSuccess}; +} + +Exception IOFile::Write(const ByteArray& data) { + if (!file_.is_open()) { + return {Exception::kIo}; + } + + if (!file_.good()) { + return {Exception::kIo}; + } + + file_.write(data.data(), data.size()); + file_.flush(); + return {file_.good() ? Exception::kSuccess : Exception::kIo}; +} + +Exception IOFile::Flush() { + file_.flush(); + return {file_.good() ? Exception::kSuccess : Exception::kIo}; +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/file.h b/internal/platform/implementation/linux/file.h new file mode 100644 index 00000000..5176f33c --- /dev/null +++ b/internal/platform/implementation/linux/file.h @@ -0,0 +1,60 @@ +// 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_LINUX_FILE_H_ +#define PLATFORM_IMPL_LINUX_FILE_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/input_file.h" +#include "internal/platform/implementation/output_file.h" + +namespace nearby { +namespace linux { + +class IOFile final : public api::InputFile, public api::OutputFile { + public: + static std::unique_ptr CreateInputFile( + const absl::string_view file_path, size_t size); + + static std::unique_ptr CreateOutputFile(const absl::string_view path); + + ExceptionOr Read(std::int64_t size) override; + + std::string GetFilePath() const override { return path_; } + + std::int64_t GetTotalSize() const override { return total_size_; } + Exception Close() override; + + Exception Write(const ByteArray& data) override; + Exception Flush() override; + + private: + explicit IOFile(const absl::string_view file_path, size_t size); + explicit IOFile(const absl::string_view file_path); + + std::fstream file_; + std::string path_; + std::int64_t total_size_; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_FILE_H_ diff --git a/internal/platform/implementation/linux/mutex.h b/internal/platform/implementation/linux/mutex.h new file mode 100644 index 00000000..f9235a16 --- /dev/null +++ b/internal/platform/implementation/linux/mutex.h @@ -0,0 +1,69 @@ +// 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. +#ifndef PLATFORM_IMPL_LINUX_MUTEX_H_ +#define PLATFORM_IMPL_LINUX_MUTEX_H_ + +#include +#include // NOLINT + +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/mutex.h" + +namespace nearby { +namespace linux { + +// A lock is a tool for controlling access to a shared resource by multiple +// threads. +// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html +class ABSL_LOCKABLE Mutex : public api::Mutex { + public: + explicit Mutex(Mode mode) : mode_(mode) {} + ~Mutex() override = default; + Mutex(Mutex&&) = delete; + Mutex& operator=(Mutex&&) = delete; + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override { + if (mode_ == Mode::kRegularNoCheck) mutex_.ForgetDeadlockInfo(); + if (mode_ == Mode::kRegular || mode_ == Mode::kRegularNoCheck) { + mutex_.Lock(); + } else { + recursive_mutex_.lock(); + } + } + + void Unlock() ABSL_UNLOCK_FUNCTION() override { + if (mode_ == Mode::kRegular || mode_ == Mode::kRegularNoCheck) { + mutex_.Unlock(); + } else { + recursive_mutex_.unlock(); + } + } + + absl::Mutex& GetMutex() { return mutex_; } + std::recursive_mutex& GetRecursiveMutex() { return recursive_mutex_; } + + private: + friend class ConditionVariable; + absl::Mutex mutex_; + std::recursive_mutex recursive_mutex_; // The actual mutex allocation + Mode mode_; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_MUTEX_H_ \ No newline at end of file diff --git a/internal/platform/implementation/linux/mutext_test.cc b/internal/platform/implementation/linux/mutext_test.cc new file mode 100644 index 00000000..466575d2 --- /dev/null +++ b/internal/platform/implementation/linux/mutext_test.cc @@ -0,0 +1,105 @@ +// 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. + +#include "internal/platform/implementation/linux/mutex.h" + +#include // NOLINT + +#include "gtest/gtest.h" + +class MutexTests : public testing::Test { + public: + class MutexTest { + public: + MutexTest(nearby::linux::Mutex& mutex) : mutex_(mutex) {} + + std::future WaitForLock() { // NOLINT + return std::async(std::launch::async, + // for this lambda you need C++14 + [this]() mutable { + absl::MutexLock(&mutex_.GetMutex()); + return true; + }); + } + + void PostEvent() { + absl::MutexLock(&mutex_.GetMutex()); + mutex_.Unlock(); + } + + private: + nearby::linux::Mutex& mutex_; + }; +}; + +TEST_F(MutexTests, SuccessfulRecursiveCreation) { + // Arrange + nearby::linux::Mutex mutex = + nearby::linux::Mutex(nearby::linux::Mutex::Mode::kRecursive); + + // Act + std::recursive_mutex& actual = mutex.GetRecursiveMutex(); + + // Assert + ASSERT_TRUE(actual.native_handle() != nullptr); +} + +TEST_F(MutexTests, SuccessfulCreation) { + // Arrange + nearby::linux::Mutex mutex(nearby::linux::Mutex::Mode::kRegular); + + // Act + absl::Mutex& actual = mutex.GetMutex(); + + // Assert + ASSERT_TRUE(&actual != nullptr); +} + +TEST_F(MutexTests, SuccessfulSignal) { + // Arrange + nearby::linux::Mutex mutex(nearby::linux::Mutex::Mode::kRegular); + + nearby::linux::Mutex& mutexRef = mutex; + MutexTest mutexTest(mutexRef); + + mutex.Lock(); + + // Act + auto result = mutexTest.WaitForLock(); + mutex.Unlock(); + + // Assert + ASSERT_TRUE(result.get()); +} + +TEST_F(MutexTests, SuccessfulRecursiveSignal) { + // Arrange + nearby::linux::Mutex mutex(nearby::linux::Mutex::Mode::kRecursive); + + nearby::linux::Mutex& mutexRef = mutex; + MutexTest mutexTest(mutexRef); + + mutex.Lock(); + mutex.Lock(); + mutex.Lock(); + + // Act + auto result = mutexTest.WaitForLock(); + mutex.Unlock(); + mutex.Unlock(); + mutex.Unlock(); + + // Assert + ASSERT_TRUE(result.get()); +} diff --git a/internal/platform/implementation/linux/test_data.h b/internal/platform/implementation/linux/test_data.h new file mode 100644 index 00000000..7cdddf9b --- /dev/null +++ b/internal/platform/implementation/linux/test_data.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef PLATFORM_IMPL_LINUX_TEST_DATA_H_ +#define PLATFORM_IMPL_LINUX_TEST_DATA_H_ + +#define INVALID_ARGUMENT_TEXT "max_concurrency" +#define THREADPOOL_MAX_SIZE_TEXT "Thread pool max size exceeded." +#define RUNNABLE_TEXT std::string("runnable ") +#define RUNNABLE_0_TEXT RUNNABLE_TEXT + std::string("0") +#define RUNNABLE_1_TEXT RUNNABLE_TEXT + std::string("1") +#define RUNNABLE_2_TEXT RUNNABLE_TEXT + std::string("2") +#define RUNNABLE_3_TEXT RUNNABLE_TEXT + std::string("3") +#define RUNNABLE_4_TEXT RUNNABLE_TEXT + std::string("4") +#define RUNNABLE_SEPARATOR_TEXT std::string(", ") +#define RUNNABLE_ALL_TEXT \ + (RUNNABLE_0_TEXT + RUNNABLE_SEPARATOR_TEXT + RUNNABLE_1_TEXT + \ + RUNNABLE_SEPARATOR_TEXT + RUNNABLE_2_TEXT + RUNNABLE_SEPARATOR_TEXT + \ + RUNNABLE_3_TEXT + RUNNABLE_SEPARATOR_TEXT + RUNNABLE_4_TEXT + \ + RUNNABLE_SEPARATOR_TEXT) + +#endif // PLATFORM_IMPL_LINUX_TEST_DATA_H_ + diff --git a/internal/platform/implementation/linux/thread_pool.cc b/internal/platform/implementation/linux/thread_pool.cc new file mode 100644 index 00000000..40dec614 --- /dev/null +++ b/internal/platform/implementation/linux/thread_pool.cc @@ -0,0 +1,128 @@ +// Copyright 2021-2023 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/linux/thread_pool.h" + +#include +#include + +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/logging.h" +#include "internal/platform/runnable.h" + +namespace nearby { +namespace linux { + +std::unique_ptr ThreadPool::Create(int max_pool_size) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Create thread pool with maximum size(" + << max_pool_size << ")."; + + if (max_pool_size <= 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Maximum pool size must be positive integer value."; + return nullptr; + } + + std::unique_ptr> thread_pool = std::make_unique>(); + + // Sets thread pool maximum value. + thread_pool->resize(max_pool_size); + + return absl::WrapUnique( + new ThreadPool(thread_pool, max_pool_size)); +} + +ThreadPool::ThreadPool(std::unique_ptr> &thread_pool, int max_pool_size) + : thread_pool_(std::move(thread_pool)), + max_pool_size_(max_pool_size) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Thread pool(" << this + << ") is created with size:" << max_pool_size_; + + for (int size = 0; size < max_pool_size_; size++) { + thread_pool_->at(size) = std::thread([this]() { + while (!tasks_.empty()) { + RunNextTask(); + // Possibly don't need but here to prevent 100% usage for loop + sleep(300); + } + }); + } +} + +ThreadPool::~ThreadPool() { + NEARBY_LOGS(VERBOSE) << __func__ << ": Thread pool(" << this + << ") is released."; + + ShutDown(); +} + +bool ThreadPool::Run(Runnable task) { + absl::MutexLock lock(&mutex_); + + if (thread_pool_->size() == max_pool_size_) { + return false; + } + + tasks_.push(std::move(task)); + NEARBY_LOGS(VERBOSE) << __func__ << ": Scheduled to run task(" + << &tasks_.back() << ")."; + return true; +} + +void ThreadPool::ShutDown() { + absl::MutexLock lock(&mutex_); + + if (!thread_pool_->empty()) { + NEARBY_LOGS(WARNING) << __func__ << ": Request to shutdown thread pool with " << thread_pool_->size() << " tasks not finished(" << this << ")."; + } + + NEARBY_LOGS(VERBOSE) << __func__ << ": Shutdown thread pool(" << this << ")."; + if (thread_pool_ == nullptr) { + NEARBY_LOGS(WARNING) << __func__ << ": Shutdown on closed thread pool(" + << this << ")."; + return; + } + + thread_pool_.reset(); +} + +void ThreadPool::RunNextTask() { + Runnable task = nullptr; + + { + absl::MutexLock lock(&mutex_); + + if (!thread_pool_) { + return; + } + if (!tasks_.empty()) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Run task(" << &tasks_.front() + << ")."; + + task = std::move(tasks_.front()); + tasks_.pop(); + } + } + if (task == nullptr) { + NEARBY_LOGS(WARNING) << __func__ + << ": Tried to run task in an empty thread pool."; + return; + } + + task(); +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/thread_pool.h b/internal/platform/implementation/linux/thread_pool.h new file mode 100644 index 00000000..889fe8b6 --- /dev/null +++ b/internal/platform/implementation/linux/thread_pool.h @@ -0,0 +1,72 @@ +// Copyright 2020-2023 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_LINUX_THREAD_POOL_H_ +#define PLATFORM_IMPL_LINUX_THREAD_POOL_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/runnable.h" + +namespace nearby { +namespace linux { + +class ThreadPool { + public: + virtual ~ThreadPool(); + static std::unique_ptr Create(int max_pool_size); + + // Runs a task on thread pool. The result indicates whether the task is put + // into the thread pool. + bool Run(Runnable task) ABSL_LOCKS_EXCLUDED(mutex_); + + // The thread pool is closed immediately if there is no outstanding work, + // I/O, timer, or wait objects that are bound to the pool; otherwise, the + // thread pool is released asynchronously after the outstanding objects are + // freed. + void ShutDown() ABSL_LOCKS_EXCLUDED(mutex_); + + private: + ThreadPool(std::unique_ptr> &thread_pool, int max_pool_size); + + void RunNextTask(); + + // Starts each task and injects a function that removes the task when it is finished + std::thread tasks_runner_; + + // Protects the access to tasks of the thread pool. + mutable absl::Mutex mutex_; + + // The task queue of the thread pool. Thread pool will pick up task to run + // when it is idle. + std::queue tasks_ ABSL_GUARDED_BY(mutex_); + + // All the threads run in the pool + std::unique_ptr> thread_pool_ ABSL_GUARDED_BY(mutex_); + + // The maximum thread count in the thread pool + int max_pool_size_ ABSL_GUARDED_BY(mutex_) = 0; + +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_THREAD_POOL_H_ diff --git a/internal/platform/implementation/linux/utils.cc b/internal/platform/implementation/linux/utils.cc new file mode 100644 index 00000000..1d22b21b --- /dev/null +++ b/internal/platform/implementation/linux/utils.cc @@ -0,0 +1,362 @@ +// 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/linux/utils.h" + +// Standard C/C++ headers +#include +#include +#include +#include +#include +#include +#include + +// Third party headers +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" + +// Nearby connections headers +#include "absl/strings/string_view.h" +#include "internal/platform/bluetooth_utils.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/crypto.h" +#include "internal/platform/logging.h" +#include "internal/platform/uuid.h" + +// Linux headers +#include +#include +#include +#include + +namespace nearby { +namespace linux { +namespace { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress) { + std::string buffer = absl::StrFormat( + "%02llx:%02llx:%02llx:%02llx:%02llx:%02llx", bluetoothAddress >> 40, + (bluetoothAddress >> 32) & 0xff, (bluetoothAddress >> 24) & 0xff, + (bluetoothAddress >> 16) & 0xff, (bluetoothAddress >> 8) & 0xff, + bluetoothAddress & 0xff); + + return absl::AsciiStrToUpper(buffer); +} + +uint64_t mac_address_string_to_uint64(absl::string_view mac_address) { + ByteArray mac_address_array = BluetoothUtils::FromString(mac_address); + uint64_t mac_address_uint64 = 0; + for (int i = 0; i < mac_address_array.size(); i++) { + mac_address_uint64 <<= 8; + mac_address_uint64 |= static_cast( + static_cast(*(mac_address_array.data() + i))); + } + return mac_address_uint64; +} + +std::string ipaddr_4bytes_to_dotdecimal_string( + absl::string_view ipaddr_4bytes) { + + union addrs { + in_addr_t addr; + uint8_t bits[4]; + } address; + + address.bits[0] = ipaddr_4bytes[0]; + address.bits[1] = ipaddr_4bytes[1]; + address.bits[2] = ipaddr_4bytes[2]; + address.bits[3] = ipaddr_4bytes[3]; + + struct in_addr addr; + + addr.s_addr = address.addr; + char* ipv4_address = inet_ntoa(addr); + if (ipv4_address == nullptr) { + return {}; + } + + return std::string(ipv4_address); +} + +std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s) { + if (ipv4_s.empty()) { + return {}; + } + + struct in_addr addr; + + if (inet_aton(ipv4_s.c_str(), &addr) != 0) { + return {}; + } + + std::string ipv4_b = std::to_string(addr.s_addr); + + return std::string(); +} + +std::wstring string_to_wstring(std::string str) { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +std::string wstring_to_string(std::wstring wstr) { + std::wstring_convert> converter; + return converter.to_bytes(wstr); +} + +std::vector GetIpv4Addresses() { + std::vector result; + + struct ifaddrs *interface = nullptr; + char host[NI_MAXHOST]; + + if (getifaddrs(&interface) != 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to get interfaces. Error: " + << strerror(errno); + freeifaddrs(interface); + return {}; + } + int status = 0; + for (struct ifaddrs *ifa = interface; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET) { + status = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST); + } + switch (status) { + case EAI_AGAIN: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : The name could not be resolved at this time. " + << "Try again later."; + break; + case EAI_BADFLAGS: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : The flags argument has an invalid value."; + break; + case EAI_FAIL: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : A nonrecoverable error occured."; + break; + case EAI_FAMILY: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : The address family was not recognized, " + << "or the address length was invalid for the " + << "specified family."; + break; + case EAI_MEMORY: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : Out of memory."; + break; + case EAI_NONAME: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : The name does not resolve for the suplied arguments." + << " NI_NAMEREQD is set and the host's name cannot be located, " + << "or neither hostname nor service name were requsted."; + break; + case EAI_OVERFLOW: + NEARBY_LOGS(ERROR) << __func__ + << "Failed to get IP for interface: " + << ifa->ifa_name + << " : The bugger pointed to by `host` or `serv` was too small."; + break; + case EAI_SYSTEM: + NEARBY_LOGS(ERROR) << __func__ + << "A system error occured. Error code: " + << errno + << ": " << strerror(errno); + break; + } + } + return result; +} + +std::vector Get4BytesIpv4Addresses() { + std::vector result; + std::vector ipv4_addresses = GetIpv4Addresses(); + for (const auto& ipv4_address : ipv4_addresses) { + // Converts IP address from x.x.x.x to 4 bytes format using utils function + result.push_back(ipaddr_dotdecimal_to_4bytes_string(ipv4_address)); + } + + return result; +} + +/* +Uuid winrt_guid_to_nearby_uuid(const ::winrt::guid& guid) { + int64_t data1 = guid.Data1; + int64_t data2 = guid.Data2; + int64_t data3 = guid.Data3; + + int64_t msb = ((data1 >> 24) & 0xff) << 56 | ((data1 >> 16) & 0xff) << 48 | + ((data1 >> 8) & 0xff) << 40 | ((data1)&0xff) << 32 | + ((data2 >> 8) & 0xff) << 24 | ((data2)&0xff) << 16 | + ((data3 >> 8) & 0xff) << 8 | (data3 & 0xff); + + int64_t lsb = + ((int64_t)guid.Data4[0]) << 56 | ((int64_t)guid.Data4[1]) << 48 | + ((int64_t)guid.Data4[2]) << 40 | ((int64_t)guid.Data4[3]) << 32 | + ((int64_t)guid.Data4[4]) << 24 | ((int64_t)guid.Data4[5]) << 16 | + ((int64_t)guid.Data4[6]) << 8 | (int64_t)guid.Data4[7]; + + return Uuid(msb, lsb); +} +*/ + +/* +winrt::guid nearby_uuid_to_winrt_guid(Uuid uuid) { + winrt::guid guid; + uint64_t msb = uuid.GetMostSigBits(); + guid.Data1 = ((msb >> 56) & 0xff) << 24 | ((msb >> 48) & 0xff) << 16 | + ((msb >> 40) & 0xff) << 8 | ((msb >> 32) & 0xff); + guid.Data2 = ((msb >> 24) & 0xff) << 8 | ((msb >> 16) & 0xff); + guid.Data3 = ((msb >> 8) & 0xff) << 8 | (msb & 0xff); + uint64_t lsb = uuid.GetLeastSigBits(); + guid.Data4[0] = (lsb >> 56) & 0xff; + guid.Data4[1] = (lsb >> 48) & 0xff; + guid.Data4[2] = (lsb >> 40) & 0xff; + guid.Data4[3] = (lsb >> 32) & 0xff; + guid.Data4[4] = (lsb >> 24) & 0xff; + guid.Data4[5] = (lsb >> 16) & 0xff; + guid.Data4[6] = (lsb >> 8) & 0xff; + guid.Data4[7] = lsb & 0xff; + return guid; +} +*/ + +/* +bool is_nearby_uuid_equal_to_winrt_guid(const Uuid& uuid, + const ::winrt::guid& guid) { + return uuid == winrt_guid_to_nearby_uuid(guid); +} +*/ + +ByteArray Sha256(absl::string_view input, size_t size) { + ByteArray hash = nearby::Crypto::Sha256(input); + return ByteArray{hash.data(), size}; +} +/* +bool InspectableReader::ReadBoolean(IInspectable inspectable) { + if (inspectable == nullptr) { + return false; + } + + auto property_value = + inspectable.try_as(); + if (property_value == nullptr) { + throw std::invalid_argument("no property value interface."); + } + if (property_value.Type() != + winrt::Windows::Foundation::PropertyType::Boolean) { + throw std::invalid_argument("not uin16 data type."); + } + + return property_value.GetBoolean(); +} + +uint16 InspectableReader::ReadUint16(IInspectable inspectable) { + if (inspectable == nullptr) { + return 0; + } + + auto property_value = + inspectable.try_as(); + if (property_value == nullptr) { + throw std::invalid_argument("no property value interface."); + } + if (property_value.Type() != + winrt::Windows::Foundation::PropertyType::UInt16) { + throw std::invalid_argument("not uin16 data type."); + } + + return property_value.GetUInt16(); +} + +uint32 InspectableReader::ReadUint32(IInspectable inspectable) { + if (inspectable == nullptr) { + return 0; + } + + auto property_value = + inspectable.try_as(); + if (property_value == nullptr) { + throw std::invalid_argument("no property value interface."); + } + if (property_value.Type() != + winrt::Windows::Foundation::PropertyType::UInt32) { + throw std::invalid_argument("not uin32 data type."); + } + + return property_value.GetUInt32(); +} + +std::string InspectableReader::ReadString(IInspectable inspectable) { + if (inspectable == nullptr) { + return ""; + } + + auto property_value = + inspectable.try_as(); + if (property_value == nullptr) { + throw std::invalid_argument("no property value interface."); + } + if (property_value.Type() != + winrt::Windows::Foundation::PropertyType::String) { + throw std::invalid_argument("not string data type."); + } + + return wstring_to_string(property_value.GetString().c_str()); +} + +std::vector InspectableReader::ReadStringArray( + IInspectable inspectable) { + std::vector result; + if (inspectable == nullptr) { + return result; + } + + auto property_value = + inspectable.try_as(); + if (property_value == nullptr) { + throw std::invalid_argument("no property value interface."); + } + if (property_value.Type() != + winrt::Windows::Foundation::PropertyType::StringArray) { + throw std::invalid_argument("not string array data type."); + } + + winrt::com_array strings; + property_value.GetStringArray(strings); + + for (winrt::hstring str : strings) { + result.push_back(winrt::to_string(str)); + } + return result; +} +*/ +} +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/utils.h b/internal/platform/implementation/linux/utils.h new file mode 100644 index 00000000..ef43e77e --- /dev/null +++ b/internal/platform/implementation/linux/utils.h @@ -0,0 +1,85 @@ +// 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_LINUX_UTILS_H_ +#define PLATFORM_IMPL_LINUX_UTILS_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/uuid.h" + +namespace nearby { +namespace linux { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress); +uint64_t mac_address_string_to_uint64(absl::string_view mac_address); + +std::string ipaddr_4bytes_to_dotdecimal_string(absl::string_view ipaddr_4bytes); +std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s); + +// Helpers to linux platform +std::wstring string_to_wstring(std::string str); +std::string wstring_to_string(std::wstring wstr); +ByteArray Sha256(absl::string_view input, size_t size); + +// Reads the IPv4 addresses +std::vector GetIpv4Addresses(); +std::vector Get4BytesIpv4Addresses(); + +/* +// Help methods to convert between Uuid and winrt::guid +Uuid winrt_guid_to_nearby_uuid(const ::winrt::guid& guid); +winrt::guid nearby_uuid_to_winrt_guid(Uuid uuid); + +// Check whether Uuid and guid is the same value. +bool is_nearby_uuid_equal_to_winrt_guid(const Uuid& uuid, + const ::winrt::guid& guid); +*/ + +namespace Constants { +// The Id of the Service Name SDP attribute +const uint16_t SdpServiceNameAttributeId = 0x100; + +// The SDP Type of the Service Name SDP attribute. +// The first byte in the SDP Attribute encodes the SDP Attribute Type as +// follows: +// - the Attribute Type size in the least significant 3 bits, +// - the SDP Attribute Type value in the most significant 5 bits. +const char SdpServiceNameAttributeType = (4 << 3) | 5; + +// Possible values for the adapter type. Refer to: +// https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_info +const uint16_t kInterfaceTypeEthernet = 6; +const uint16_t kInterfaceTypeWifi = 71; +} // namespace Constants + +/* +class InspectableReader { + public: + static bool ReadBoolean(IInspectable inspectable); + static uint16_t ReadUint16(IInspectable inspectable); + static uint32_t ReadUint32(IInspectable inspectable); + static std::string ReadString(IInspectable inspectable); + static std::vector ReadStringArray(IInspectable inspectable); +}; +*/ + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_UTILS_H_