From fe26466278537f9904beefae94d0950d57f5fee6 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Thu, 15 Dec 2022 16:29:18 -0800 Subject: [PATCH] Migrate fast pair from location/nearby/cpp/fastpair to third_party/nearby/fastpair PiperOrigin-RevId: 495726740 --- fastpair/internal/api/BUILD | 12 ++ fastpair/internal/api/device_info.h | 60 ++++++ fastpair/internal/api/fast_pair_platform.h | 42 ++++ fastpair/internal/api/timer.h | 53 +++++ fastpair/internal/base/BUILD | 48 +++++ fastpair/internal/base/bluetooth_address.cc | 124 +++++++++++ fastpair/internal/base/bluetooth_address.h | 53 +++++ .../internal/base/bluetooth_address_test.cc | 61 ++++++ fastpair/internal/base/observer_list.h | 62 ++++++ fastpair/internal/impl/g3/BUILD | 37 ++++ fastpair/internal/impl/g3/device_info.h | 58 +++++ fastpair/internal/impl/g3/device_info_test.cc | 76 +++++++ .../internal/impl/g3/fast_pair_platform.cc | 37 ++++ fastpair/internal/impl/g3/timer.h | 73 +++++++ fastpair/internal/impl/windows/BUILD | 41 +++- fastpair/internal/impl/windows/device_info.cc | 202 ++++++++++++++++++ fastpair/internal/impl/windows/device_info.h | 55 +++++ .../internal/impl/windows/device_info_test.cc | 95 ++++++++ .../impl/windows/fast_pair_platform.cc | 40 ++++ fastpair/internal/impl/windows/timer.cc | 114 ++++++++++ fastpair/internal/impl/windows/timer.h | 51 +++++ fastpair/internal/impl/windows/timer_test.cc | 69 ++++++ fastpair/internal/impl/windows/utils.cc | 57 +++++ fastpair/internal/impl/windows/utils.h | 39 ++++ fastpair/internal/public/BUILD | 87 ++++++++ fastpair/internal/public/device_info.h | 47 ++++ fastpair/internal/public/device_info_impl.cc | 45 ++++ fastpair/internal/public/device_info_impl.h | 51 +++++ fastpair/internal/public/task_runner.h | 48 +++++ fastpair/internal/public/task_runner_impl.cc | 82 +++++++ fastpair/internal/public/task_runner_impl.h | 56 +++++ .../internal/public/task_runner_impl_test.cc | 166 ++++++++++++++ fastpair/internal/public/timer.h | 51 +++++ fastpair/internal/public/timer_impl.cc | 68 ++++++ fastpair/internal/public/timer_impl.h | 48 +++++ fastpair/internal/public/timer_impl_test.cc | 62 ++++++ .../platform/implementation/windows/BUILD | 1 + 37 files changed, 2370 insertions(+), 1 deletion(-) create mode 100644 fastpair/internal/api/BUILD create mode 100644 fastpair/internal/api/device_info.h create mode 100644 fastpair/internal/api/fast_pair_platform.h create mode 100644 fastpair/internal/api/timer.h create mode 100644 fastpair/internal/base/BUILD create mode 100644 fastpair/internal/base/bluetooth_address.cc create mode 100644 fastpair/internal/base/bluetooth_address.h create mode 100644 fastpair/internal/base/bluetooth_address_test.cc create mode 100644 fastpair/internal/base/observer_list.h create mode 100644 fastpair/internal/impl/g3/BUILD create mode 100644 fastpair/internal/impl/g3/device_info.h create mode 100644 fastpair/internal/impl/g3/device_info_test.cc create mode 100644 fastpair/internal/impl/g3/fast_pair_platform.cc create mode 100644 fastpair/internal/impl/g3/timer.h create mode 100644 fastpair/internal/impl/windows/device_info.cc create mode 100644 fastpair/internal/impl/windows/device_info.h create mode 100644 fastpair/internal/impl/windows/device_info_test.cc create mode 100644 fastpair/internal/impl/windows/fast_pair_platform.cc create mode 100644 fastpair/internal/impl/windows/timer.cc create mode 100644 fastpair/internal/impl/windows/timer.h create mode 100644 fastpair/internal/impl/windows/timer_test.cc create mode 100644 fastpair/internal/impl/windows/utils.cc create mode 100644 fastpair/internal/impl/windows/utils.h create mode 100644 fastpair/internal/public/BUILD create mode 100644 fastpair/internal/public/device_info.h create mode 100644 fastpair/internal/public/device_info_impl.cc create mode 100644 fastpair/internal/public/device_info_impl.h create mode 100644 fastpair/internal/public/task_runner.h create mode 100644 fastpair/internal/public/task_runner_impl.cc create mode 100644 fastpair/internal/public/task_runner_impl.h create mode 100644 fastpair/internal/public/task_runner_impl_test.cc create mode 100644 fastpair/internal/public/timer.h create mode 100644 fastpair/internal/public/timer_impl.cc create mode 100644 fastpair/internal/public/timer_impl.h create mode 100644 fastpair/internal/public/timer_impl_test.cc diff --git a/fastpair/internal/api/BUILD b/fastpair/internal/api/BUILD new file mode 100644 index 00000000..61480fd4 --- /dev/null +++ b/fastpair/internal/api/BUILD @@ -0,0 +1,12 @@ +licenses(["notice"]) + +cc_library( + name = "platform", + textual_hdrs = glob(["**/*.h"]), + visibility = [ + "//fastpair:__subpackages__", + ], + deps = [ + "@com_google_absl//absl/strings", + ], +) diff --git a/fastpair/internal/api/device_info.h b/fastpair/internal/api/device_info.h new file mode 100644 index 00000000..6e5cd8c1 --- /dev/null +++ b/fastpair/internal/api/device_info.h @@ -0,0 +1,60 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_DEVICE_INFO_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_DEVICE_INFO_H_ + +#include +#include + +#include "absl/strings/string_view.h" + +namespace location { +namespace nearby { +namespace api { + +class DeviceInfo { + public: + enum class ScreenStatus { + kUndetermined = 0, + kLocked = 1, + kUnlocked = 2, + }; + + enum class OsType { + kUnknown = 0, + kAndroid = 1, + kChromeOs = 2, + kIos = 3, + kWindows = 4, + }; + + virtual ~DeviceInfo() = default; + + virtual OsType GetOsType() const = 0; + + // Monitor screen status + virtual bool IsScreenLocked() const = 0; + virtual void RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) = 0; + virtual void UnregisterScreenLockedListener( + absl::string_view listener_name) = 0; +}; + +} // namespace api +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_DEVICE_INFO_H_ diff --git a/fastpair/internal/api/fast_pair_platform.h b/fastpair/internal/api/fast_pair_platform.h new file mode 100644 index 00000000..a946df6c --- /dev/null +++ b/fastpair/internal/api/fast_pair_platform.h @@ -0,0 +1,42 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_FAST_PAIR_PLATFORM_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_FAST_PAIR_PLATFORM_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "fastpair/internal/api/device_info.h" +#include "fastpair/internal/api/timer.h" + +namespace location { +namespace nearby { +namespace api { + +class ImplementationFastPairPlatform { + public: + static std::unique_ptr CreateDeviceInfo(); + + // Timer API + static std::unique_ptr CreateTimer(); +}; + +} // namespace api +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_API_FAST_PAIR_PLATFORM_H_ diff --git a/fastpair/internal/api/timer.h b/fastpair/internal/api/timer.h new file mode 100644 index 00000000..e7ba7e2e --- /dev/null +++ b/fastpair/internal/api/timer.h @@ -0,0 +1,53 @@ +// Copyright 2022 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 LOCATION_NEARBY_CPP_FASTPAIR_INTERNAL_API_TIMER_H_ +#define LOCATION_NEARBY_CPP_FASTPAIR_INTERNAL_API_TIMER_H_ + +#include + +namespace location { +namespace nearby { +namespace api { + +class Timer { + public: + virtual ~Timer() = default; + + // Creates a timer based on interval. + // + // @param delay + // The amount of time in milliseconds relative to the current + // time that must elapse before the timer is signaled for the first + // time. + // @param interval + // The period of the timer, in milliseconds. If this parameter + // is zero, the timer is signaled once. + // @param callback + // It will be called when timer signaled. + // @return + // return true if success, otherwise false + virtual bool Create(int delay, int interval, + std::function callback) = 0; + + // Stops timer. No timer signal is sent after the call. + virtual bool Stop() = 0; + virtual bool FireNow() = 0; +}; + +} // namespace api +} // namespace nearby +} // namespace location + +#endif // LOCATION_NEARBY_CPP_FASTPAIR_INTERNAL_API_TIMER_H_ diff --git a/fastpair/internal/base/BUILD b/fastpair/internal/base/BUILD new file mode 100644 index 00000000..c8a85e15 --- /dev/null +++ b/fastpair/internal/base/BUILD @@ -0,0 +1,48 @@ +licenses(["notice"]) + +cc_library( + name = "bluetooth_address", + srcs = [ + "bluetooth_address.cc", + ], + hdrs = [ + "bluetooth_address.h", + ], + visibility = [ + "//fastpair:__subpackages__", + ], + deps = [ + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "base", + srcs = [ + ], + hdrs = [ + "observer_list.h", + ], + visibility = [ + "//fastpair:__subpackages__", + ], + deps = ["@com_google_absl//absl/container:flat_hash_set"], +) + +cc_test( + name = "base_test", + size = "small", + timeout = "short", + srcs = [ + "bluetooth_address_test.cc", + ], + shard_count = 8, + deps = [ + ":bluetooth_address", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/internal/base/bluetooth_address.cc b/fastpair/internal/base/bluetooth_address.cc new file mode 100644 index 00000000..bb2b10b1 --- /dev/null +++ b/fastpair/internal/base/bluetooth_address.cc @@ -0,0 +1,124 @@ +// Copyright 2022 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 "fastpair/internal/base/bluetooth_address.h" + +#include +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" + +namespace location { +namespace nearby { +namespace fastpair { +namespace { + +// Utility to convert a character to a digit in a given base +template +std::optional CharToDigit(CHAR c) { + static_assert(1 <= BASE && BASE <= 36, "BASE needs to be in [1, 36]"); + if (c >= '0' && c < '0' + std::min(BASE, 10)) return c - '0'; + + if (c >= 'a' && c < 'a' + BASE - 10) return c - 'a' + 10; + + if (c >= 'A' && c < 'A' + BASE - 10) return c - 'A' + 10; + + return std::nullopt; +} + +// Convert a string of hex digits into an equivalent byte array +template +static bool HexStringToByteContainer(absl::string_view input, OutIter output) { + size_t count = input.size(); + if (count == 0 || (count % 2) != 0) return false; + for (uintptr_t i = 0; i < count / 2; ++i) { + std::optional upper = CharToDigit<16>(input[i * 2]); + std::optional lower = CharToDigit<16>(input[i * 2 + 1]); + if (!upper.has_value() || !lower.has_value()) { + return false; + } + *(output++) = (*upper << 4) | *lower; + } + return true; +} + +bool HexStringToSpan(absl::string_view input, absl::Span output) { + if (input.size() / 2 != output.size()) return false; + + return HexStringToByteContainer(input, output.begin()); +} + +} // namespace + +bool ParseBluetoothAddress(absl::string_view input, + absl::Span output) { + if (output.size() != 6) return false; + + // Try parsing addresses that lack separators, like "1A2B3C4D5E6F". + if (input.size() == 12) return HexStringToSpan(input, output); + + // Try parsing MAC address with separators like: "00:11:22:33:44:55" or + // "00-11-22-33-44-55". Separator can be either '-' or ':', but must use the + // same style throughout. + if (input.size() == 17) { + const char separator = input[2]; + if (separator != '-' && separator != ':') return false; + return (input[2] == separator) && (input[5] == separator) && + (input[8] == separator) && (input[11] == separator) && + (input[14] == separator) && + HexStringToSpan(input.substr(0, 2), output.subspan(0, 1)) && + HexStringToSpan(input.substr(3, 2), output.subspan(1, 1)) && + HexStringToSpan(input.substr(6, 2), output.subspan(2, 1)) && + HexStringToSpan(input.substr(9, 2), output.subspan(3, 1)) && + HexStringToSpan(input.substr(12, 2), output.subspan(4, 1)) && + HexStringToSpan(input.substr(15, 2), output.subspan(5, 1)); + } + + return false; +} + +std::string ConvertBluetoothAddressUIntToString(uint64_t address) { + std::string mac_address = absl::StrFormat( + "%02llX:%02llX:%02llX:%02llX:%02llX:%02llX", address >> 40, + (address >> 32) & 0xff, (address >> 24) & 0xff, (address >> 16) & 0xff, + (address >> 8) & 0xff, address & 0xff); + return CanonicalizeBluetoothAddress(mac_address); +} + +std::string CanonicalizeBluetoothAddress(absl::string_view address) { + std::array bytes; + + if (!ParseBluetoothAddress(address, absl::MakeSpan(bytes.data(), 6))) + return std::string(); + + return CanonicalizeBluetoothAddress(bytes); +} + +std::string CanonicalizeBluetoothAddress( + const std::array& address_bytes) { + return absl::StrFormat("%02X:%02X:%02X:%02X:%02X:%02X", address_bytes[0], + address_bytes[1], address_bytes[2], address_bytes[3], + address_bytes[4], address_bytes[5]); +} + +std::string CanonicalizeBluetoothAddress(uint64_t address) { + return ConvertBluetoothAddressUIntToString(address); +} + +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/base/bluetooth_address.h b/fastpair/internal/base/bluetooth_address.h new file mode 100644 index 00000000..eaac21c9 --- /dev/null +++ b/fastpair/internal/base/bluetooth_address.h @@ -0,0 +1,53 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_BLUETOOTH_ADDRESS_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_BLUETOOTH_ADDRESS_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace location { +namespace nearby { +namespace fastpair { + +// Parses a Bluetooth address to an output buffer. The output buffer must be +// exactly 6 bytes in size. +// The address can be formatted in one of three ways: +// · 1A:2B:3C:4D:5E:6F +// · 1A-2B-3C-4D-5E-6F +// · 1A2B3C4D5E6F +bool ParseBluetoothAddress(absl::string_view input, absl::Span output); + +// Converts a uint64_t Bluetooth address to string. +std::string ConvertBluetoothAddressUIntToString(uint64_t address); + +// Returns |address| in the canonical format: XX:XX:XX:XX:XX:XX, where each 'X' +// is a hex digit. If the input |address| is invalid, returns an empty string. +std::string CanonicalizeBluetoothAddress(absl::string_view address); +std::string CanonicalizeBluetoothAddress( + const std::array& address_bytes); +std::string CanonicalizeBluetoothAddress(uint64_t address); + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_BLUETOOTH_ADDRESS_H_ diff --git a/fastpair/internal/base/bluetooth_address_test.cc b/fastpair/internal/base/bluetooth_address_test.cc new file mode 100644 index 00000000..51d90e73 --- /dev/null +++ b/fastpair/internal/base/bluetooth_address_test.cc @@ -0,0 +1,61 @@ +// Copyright 2022 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 "fastpair/internal/base/bluetooth_address.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/types/span.h" + +namespace location { +namespace nearby { +namespace fastpair { +namespace { + +TEST(BluetoothUtil, ParseBluetoothAddress) { + std::array output; + std::array expected_output{{26, 43, 60, 77, 94, 111}}; + EXPECT_TRUE(ParseBluetoothAddress( + "1A:2B:3C:4D:5E:6F", absl::MakeSpan(output.data(), output.size()))); + EXPECT_EQ(output, expected_output); +} + +TEST(BluetoothUtil, ConvertBluetoothAddressUIntToString) { + const uint64_t input = 0x00001A2B3C4D5E6F; + std::string expected_output = "1A:2B:3C:4D:5E:6F"; + std::string output = ConvertBluetoothAddressUIntToString(input); + EXPECT_EQ(output, expected_output); +} + +TEST(BluetoothUtil, CanonicalizeBluetoothAddress) { + std::array address{{26, 43, 60, 77, 94, 111}}; + EXPECT_EQ(CanonicalizeBluetoothAddress(address), "1A:2B:3C:4D:5E:6F"); + EXPECT_EQ(CanonicalizeBluetoothAddress("1A-2B-3C-4D-5E-6F"), + "1A:2B:3C:4D:5E:6F"); + EXPECT_EQ(CanonicalizeBluetoothAddress(0x00001A2B3C4D5E6F), + "1A:2B:3C:4D:5E:6F"); + + // Canonicalizes invalid address + EXPECT_EQ(CanonicalizeBluetoothAddress("1A-2B-3C-4D-5E-6F-89"), ""); + EXPECT_EQ(CanonicalizeBluetoothAddress("nearby"), ""); + EXPECT_EQ(CanonicalizeBluetoothAddress("MA-2M-3C-4D-5E-6F"), ""); + EXPECT_EQ(CanonicalizeBluetoothAddress(0x001A2B3C4D5E6F89), ""); +} + +} // namespace +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/base/observer_list.h b/fastpair/internal/base/observer_list.h new file mode 100644 index 00000000..71b3028e --- /dev/null +++ b/fastpair/internal/base/observer_list.h @@ -0,0 +1,62 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_OBSERVER_LIST_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_OBSERVER_LIST_H_ + +#include +#include + +#include "absl/container/flat_hash_set.h" + +namespace location { +namespace nearby { +namespace fastpair { + +template +class ObserverList { + public: + using iterator = typename absl::flat_hash_set::iterator; + using const_iterator = + typename absl::flat_hash_set::const_iterator; + + void AddObserver(ObserverType* observer) { observers_.emplace(observer); } + + void RemoveObserver(ObserverType* observer) { observers_.erase(observer); } + + bool HasObserver(ObserverType* observer) { + return observers_.contains(observer); + } + + void Clear() { observers_.clear(); } + + bool empty() const { return observers_.empty(); } + + int size() const { return observers_.size(); } + + // Supports iterators + iterator begin() { return observers_.begin(); } + iterator end() { return observers_.end(); } + const_iterator begin() const { return observers_.begin(); } + const_iterator end() const { return observers_.end(); } + + private: + absl::flat_hash_set observers_; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BASE_OBSERVER_LIST_H_ diff --git a/fastpair/internal/impl/g3/BUILD b/fastpair/internal/impl/g3/BUILD new file mode 100644 index 00000000..3e27aed7 --- /dev/null +++ b/fastpair/internal/impl/g3/BUILD @@ -0,0 +1,37 @@ +licenses(["notice"]) + +cc_library( + name = "platform_g3", + srcs = [ + "fast_pair_platform.cc", + ], + hdrs = [ + "device_info.h", + "timer.h", + ], + visibility = [ + "//fastpair:__subpackages__", + ], + deps = [ + "//fastpair/internal/api:platform", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "g3_test", + size = "small", + timeout = "short", + srcs = [ + "device_info_test.cc", + ], + shard_count = 8, + deps = [ + ":platform_g3", + "//fastpair/internal/api:platform", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/synchronization", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/internal/impl/g3/device_info.h b/fastpair/internal/impl/g3/device_info.h new file mode 100644 index 00000000..e606c486 --- /dev/null +++ b/fastpair/internal/impl/g3/device_info.h @@ -0,0 +1,58 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_DEVICE_INFO_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_DEVICE_INFO_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "fastpair/internal/api/device_info.h" + +namespace location { +namespace nearby { +namespace g3 { + +class DeviceInfo : public api::DeviceInfo { + public: + api::DeviceInfo::OsType GetOsType() const override { + return api::DeviceInfo::OsType::kChromeOs; + } + + bool IsScreenLocked() const override { return false; } + + void RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) override { + screen_locked_listeners_.emplace(listener_name, std::move(callback)); + } + + void UnregisterScreenLockedListener( + absl::string_view listener_name) override { + screen_locked_listeners_.erase(listener_name); + } + + absl::flat_hash_map> + screen_locked_listeners_; +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_DEVICE_INFO_H_ diff --git a/fastpair/internal/impl/g3/device_info_test.cc b/fastpair/internal/impl/g3/device_info_test.cc new file mode 100644 index 00000000..1f5f8de7 --- /dev/null +++ b/fastpair/internal/impl/g3/device_info_test.cc @@ -0,0 +1,76 @@ +// Copyright 2022 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 "fastpair/internal/impl/g3/device_info.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "fastpair/internal/api/device_info.h" + +namespace location { +namespace nearby { +namespace g3 { +namespace { + +TEST(DeviceInfo, GetOsType) { + EXPECT_EQ(DeviceInfo().GetOsType(), api::DeviceInfo::OsType::kChromeOs); +} + +TEST(DeviceInfo, IsScreenLocked) { + EXPECT_FALSE(DeviceInfo().IsScreenLocked()); +} + +TEST(DeviceInfo, RegisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); +} + +TEST(DeviceInfo, UnregisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); + + device_info.UnregisterScreenLockedListener("listener_1"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.UnregisterScreenLockedListener("listener_2"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); +} + +} // namespace +} // namespace g3 +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/g3/fast_pair_platform.cc b/fastpair/internal/impl/g3/fast_pair_platform.cc new file mode 100644 index 00000000..e4b95460 --- /dev/null +++ b/fastpair/internal/impl/g3/fast_pair_platform.cc @@ -0,0 +1,37 @@ +// Copyright 2022 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 "fastpair/internal/api/fast_pair_platform.h" + +#include + +#include "fastpair/internal/impl/g3/device_info.h" +#include "fastpair/internal/impl/g3/timer.h" + +namespace location { +namespace nearby { +namespace api { + +std::unique_ptr +ImplementationFastPairPlatform::CreateDeviceInfo() { + return std::make_unique(); +} + +std::unique_ptr ImplementationFastPairPlatform::CreateTimer() { + return std::make_unique(); +} + +} // namespace api +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/g3/timer.h b/fastpair/internal/impl/g3/timer.h new file mode 100644 index 00000000..898d6b50 --- /dev/null +++ b/fastpair/internal/impl/g3/timer.h @@ -0,0 +1,73 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_TIMER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_TIMER_H_ + +#include +#include + +#include "fastpair/internal/api/timer.h" + +namespace location { +namespace nearby { +namespace g3 { + +class Timer : public api::Timer { + public: + Timer() = default; + ~Timer() override = default; + + bool Create(int delay, int interval, + std::function callback) override { + if (delay < 0 || interval < 0) { + return false; + } + callback_ = std::move(callback); + is_stopped_ = false; + return true; + } + + bool Stop() override { + is_stopped_ = true; + return true; + } + + bool FireNow() override { + if (is_stopped_ || !callback_) { + return false; + } + callback_(); + return true; + } + + // Mocked methods for test only + void TriggerCallback() { + if (is_stopped_ || callback_ == nullptr) { + return; + } + + callback_(); + } + + private: + std::function callback_; + bool is_stopped_ = false; +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_G3_TIMER_H_ diff --git a/fastpair/internal/impl/windows/BUILD b/fastpair/internal/impl/windows/BUILD index f50b4ce3..31619c93 100644 --- a/fastpair/internal/impl/windows/BUILD +++ b/fastpair/internal/impl/windows/BUILD @@ -27,23 +27,62 @@ cc_library( name = "platform_windows", srcs = [ "ble_gatt_client.cc", + "device_info.cc", + "fast_pair_platform.cc", + "timer.cc", + "utils.cc", ], hdrs = [ "ble_gatt_client.h", + "device_info.h", + "timer.h", + "utils.h", ], copts = [ - "-Ithird_party", "-Ithird_party/nearby/internal/platform/implementation/windows/generated", ], defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"], visibility = ["//fastpair:__subpackages__"], deps = [ + "//fastpair/internal/api:platform", + "//fastpair/internal/base", + "//fastpair/internal/base:bluetooth_address", "//internal/platform:base", "//internal/platform:logging", + "//internal/platform/implementation/windows", "//internal/platform/implementation/windows/generated:types", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + ], +) + +cc_test( + name = "platform_windows_test", + size = "small", + timeout = "short", + srcs = [ + "device_info_test.cc", + "timer_test.cc", + ], + copts = [ + "-Ithird_party/nearby/internal/platform/implementation/windows/generated", + ], + shard_count = 1, + deps = [ + ":platform_windows", + ":platform_windows_libs", + "//fastpair/internal/api:platform", + "//internal/platform:base", + "//internal/platform:logging", + "//internal/platform/implementation/windows", + "//internal/platform/implementation/windows/generated:types", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/internal/impl/windows/device_info.cc b/fastpair/internal/impl/windows/device_info.cc new file mode 100644 index 00000000..688018b3 --- /dev/null +++ b/fastpair/internal/impl/windows/device_info.cc @@ -0,0 +1,202 @@ +// Copyright 2022 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 "fastpair/internal/impl/windows/device_info.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "fastpair/internal/api/device_info.h" +#include "internal/platform/logging.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Foundation.h" +#include "winrt/Windows.System.h" + +namespace location { +namespace nearby { +namespace windows { + +constexpr char window_class_name[] = "FastPairDLL_MessageWindowClass"; +constexpr char window_name[] = "FastPairDLL_MessageWindow"; + +namespace { +// This WindowProc method must be static for the successful initialization of +// WNDCLASS +// window_class.lpfnWndProc = (WNDPROC) &DeviceInfo::WindowProc; +// where a WNDPROC typed function pointer is expected +// typedef LRESULT (CALLBACK* WNDPROC)(HWND,UINT,WPARAM,LPARAM) +// the calling convention used here CALLBACK is a macro defined as +// #define CALLBACK __stdcall +// +// If WindProc is not static and defined as a member function, it uses the +// __thiscall calling convention instead +// https://docs.microsoft.com/en-us/cpp/cpp/thiscall?view=msvc-170 +// https://isocpp.org/wiki/faq/pointers-to-members +// https://en.cppreference.com/w/cpp/language/pointer +// +// This is problematic because the function pointer now looks like this +// typedef LRESULT (CALLBACK* DeviceInfo_WNDPROC)(DeviceInfo* +// this,HWND,UINT,WPARAM,LPARAM) +// which causes casting errors +LRESULT CALLBACK WindowProc(HWND window_handle, UINT message, WPARAM wparam, + LPARAM lparam) { + DeviceInfo* self = reinterpret_cast( + GetWindowLongPtr(window_handle, GWLP_USERDATA)); + CREATESTRUCT* create_struct = reinterpret_cast(lparam); + LONG_PTR result = 0L; + switch (message) { + case WM_CREATE: + self = reinterpret_cast(create_struct->lpCreateParams); + self->message_window_handle_ = window_handle; + + // Store pointer to the self to the window's user data. + SetLastError(0); + result = SetWindowLongPtr(window_handle, GWLP_USERDATA, + reinterpret_cast(self)); + if (result == 0 && GetLastError() != 0) { + NEARBY_LOGS(ERROR) + << __func__ + << ": Error connecting message window to Fast Pair DLL."; + } + break; + case WM_WTSSESSION_CHANGE: + if (self) { + switch (wparam) { + case WTS_SESSION_LOCK: + for (auto& listener : self->screen_locked_listeners_) { + listener.second(api::DeviceInfo::ScreenStatus:: + kLocked); // Trigger registered callbacks + } + break; + case WTS_SESSION_UNLOCK: + for (auto& listener : self->screen_locked_listeners_) { + listener.second(api::DeviceInfo::ScreenStatus:: + kUnlocked); // Trigger registered callbacks + } + break; + } + } + break; + case WM_DESTROY: + SetLastError(0); + result = SetWindowLongPtr(window_handle, GWLP_USERDATA, NULL); + if (result == 0 && GetLastError() != 0) { + NEARBY_LOGS(ERROR) + << __func__ + << ": Error disconnecting message window to Fast Pair DLL."; + } + break; + } + + return DefWindowProc(window_handle, message, wparam, lparam); +} +} // namespace + +DeviceInfo::~DeviceInfo() { + UnregisterClass(MAKEINTATOM(registered_class_), instance_); +} + +api::DeviceInfo::OsType DeviceInfo::GetOsType() const { + return api::DeviceInfo::OsType::kWindows; +} + +bool DeviceInfo::IsScreenLocked() const { + DWORD session_id = WTSGetActiveConsoleSessionId(); + WTS_INFO_CLASS wts_info_class = WTSSessionInfoEx; + LPTSTR session_info_buffer = nullptr; + DWORD session_info_buffer_size_bytes = 0; + + WTSINFOEXW* wts_info = nullptr; + LONG session_state = WTS_SESSIONSTATE_UNKNOWN; + + if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session_id, + wts_info_class, &session_info_buffer, + &session_info_buffer_size_bytes)) { + if (session_info_buffer_size_bytes > 0) { + wts_info = (WTSINFOEXW*)session_info_buffer; + if (wts_info->Level == 1) { + session_state = wts_info->Data.WTSInfoExLevel1.SessionFlags; + } + } + WTSFreeMemory(session_info_buffer); + session_info_buffer = nullptr; + } + bool isScreenLocked = session_state == WTS_SESSIONSTATE_LOCK; + + NEARBY_LOGS(INFO) << __func__ << "ScreenStatus: " + << (isScreenLocked ? "Locked" : "NotLocked"); + + return isScreenLocked; +} + +void DeviceInfo::RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) { + if (message_window_handle_ == nullptr) { + instance_ = (HINSTANCE)GetModuleHandle(NULL); + + WNDCLASS window_class; + window_class.style = 0; + window_class.lpfnWndProc = (WNDPROC)&WindowProc; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance_; + window_class.hIcon = nullptr; + window_class.hCursor = nullptr; + window_class.hbrBackground = nullptr; + window_class.lpszMenuName = nullptr; + window_class.lpszClassName = window_class_name; + + registered_class_ = RegisterClass(&window_class); + + message_window_handle_ = CreateWindow( + MAKEINTATOM(registered_class_), // class atom from RegisterClass + window_name, // window name + 0, // window style + 0, // initial x position of window + 0, // initial y position of window + 0, // width + 0, // height + HWND_MESSAGE, // handle to the parent of window + // (message-only window in this case) + nullptr, // handle to a menu + instance_, // handle to the instance of the module + // associated to the window + this); // pointer to be passed to the window for additional data + + if (!message_window_handle_) { + NEARBY_LOGS(ERROR) + << __func__ << ": Failed to create message window for Fast Pair DLL."; + } + } + + screen_locked_listeners_.emplace(listener_name, callback); +} + +void DeviceInfo::UnregisterScreenLockedListener( + absl::string_view listener_name) { + screen_locked_listeners_.erase(listener_name); +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/device_info.h b/fastpair/internal/impl/windows/device_info.h new file mode 100644 index 00000000..7b11c8ff --- /dev/null +++ b/fastpair/internal/impl/windows/device_info.h @@ -0,0 +1,55 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_DEVICE_INFO_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_DEVICE_INFO_H_ + +#include +#include + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "fastpair/internal/api/device_info.h" + +namespace location { +namespace nearby { +namespace windows { + +class DeviceInfo : public api::DeviceInfo { + public: + ~DeviceInfo() override; + + api::DeviceInfo::OsType GetOsType() 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_; + + HINSTANCE instance_ = nullptr; + ATOM registered_class_ = NULL; + HWND message_window_handle_ = nullptr; +}; + +} // namespace windows +} // namespace nearby +} // namespace location +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_DEVICE_INFO_H_ diff --git a/fastpair/internal/impl/windows/device_info_test.cc b/fastpair/internal/impl/windows/device_info_test.cc new file mode 100644 index 00000000..8609dd7d --- /dev/null +++ b/fastpair/internal/impl/windows/device_info_test.cc @@ -0,0 +1,95 @@ +// Copyright 2022 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 "fastpair/internal/impl/windows/device_info.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "fastpair/internal/api/device_info.h" + +namespace location { +namespace nearby { +namespace windows { +namespace { + +TEST(DeviceInfo, DISABLED_GetOsType) { + EXPECT_EQ(DeviceInfo().GetOsType(), api::DeviceInfo::OsType::kWindows); +} + +TEST(DeviceInfo, DISABLED_IsScreenLocked) { + EXPECT_TRUE(DeviceInfo().IsScreenLocked()); +} + +TEST(DeviceInfo, DISABLED_RegisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); +} + +TEST(DeviceInfo, DISABLED_UnregisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); + + device_info.UnregisterScreenLockedListener("listener_1"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.UnregisterScreenLockedListener("listener_2"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); +} + +TEST(DeviceInfo, DISABLED_UpdateScreenLockedListener) { + absl::Notification notification; + + api::DeviceInfo::ScreenStatus screen_locked_tracker = + api::DeviceInfo::ScreenStatus::kUndetermined; + + std::function listener = + [&screen_locked_tracker, + ¬ification](api::DeviceInfo::ScreenStatus status) { + screen_locked_tracker = api::DeviceInfo::ScreenStatus::kLocked; + notification.Notify(); + }; + + DeviceInfo device_info; + device_info.RegisterScreenLockedListener("listener", listener); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(5))); + EXPECT_EQ(screen_locked_tracker, api::DeviceInfo::ScreenStatus::kLocked); +} + +} // namespace +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/fast_pair_platform.cc b/fastpair/internal/impl/windows/fast_pair_platform.cc new file mode 100644 index 00000000..4e7ee149 --- /dev/null +++ b/fastpair/internal/impl/windows/fast_pair_platform.cc @@ -0,0 +1,40 @@ +// Copyright 2022 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 "fastpair/internal/api/fast_pair_platform.h" + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "fastpair/internal/impl/windows/device_info.h" +#include "fastpair/internal/impl/windows/timer.h" + +namespace location { +namespace nearby { +namespace api { + +std::unique_ptr +ImplementationFastPairPlatform::CreateDeviceInfo() { + return std::make_unique(); +} + +std::unique_ptr ImplementationFastPairPlatform::CreateTimer() { + return std::make_unique(); +} + +} // namespace api +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/timer.cc b/fastpair/internal/impl/windows/timer.cc new file mode 100644 index 00000000..9533988d --- /dev/null +++ b/fastpair/internal/impl/windows/timer.cc @@ -0,0 +1,114 @@ +// Copyright 2022 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 "fastpair/internal/impl/windows/timer.h" + +#include +#include +#include + +#include "internal/platform/logging.h" + +namespace location { +namespace nearby { +namespace windows { + +Timer::~Timer() { Stop(); } + +bool Timer::Create(int delay, int interval, std::function callback) { + if ((delay < 0) || (interval < 0)) { + NEARBY_LOGS(WARNING) << "Delay and interval shouldn\'t be negative value."; + return false; + } + + if (timer_queue_handle_ != nullptr) { + return false; + } + + // Creates a queue for timers. + // If succeeds, return a handle to the timer queue. + // If fails, the return value is NULL. + timer_queue_handle_ = CreateTimerQueue(); + if (timer_queue_handle_ == nullptr) { + NEARBY_LOGS(ERROR) << "Failed to create timer queue."; + return false; + } + + delay_ = delay; + interval_ = interval; + callback_ = std::move(callback); + + // Creates a timer-queue timer. This timer expires at the specified due time, + // then after every specified period. When the timer expires, + // the callback function is called. + if (!CreateTimerQueueTimer(&handle_, timer_queue_handle_, + static_cast(TimerRoutine), + &callback_, delay, interval, WT_EXECUTEDEFAULT)) { + // Deletes a timer queue. + // Any pending timers in the queue are canceled and deleted. + if (!DeleteTimerQueueEx(timer_queue_handle_, nullptr)) { + NEARBY_LOGS(ERROR) << "Failed to create timer in timer queue."; + } + timer_queue_handle_ = nullptr; + return false; + } + + return true; +} + +bool Timer::Stop() { + if (timer_queue_handle_ == nullptr) { + return true; + } + + // Deletes a timer queue. + // Any pending timers in the queue are canceled and deleted. + if (!DeleteTimerQueueTimer(timer_queue_handle_, handle_, nullptr)) { + if (GetLastError() != ERROR_IO_PENDING) { + NEARBY_LOGS(ERROR) << "Failed to delete timer from timer queue."; + return false; + } + } + + handle_ = nullptr; + + if (!DeleteTimerQueueEx(timer_queue_handle_, nullptr)) { + NEARBY_LOGS(ERROR) << "Failed to delete timer queue."; + return false; + } + + timer_queue_handle_ = nullptr; + return true; +} + +bool Timer::FireNow() { + if (!timer_queue_handle_ || !callback_) { + return false; + } + + callback_(); + return true; +} + +void CALLBACK Timer::TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { + std::function* callback = + reinterpret_cast*>(lpParam); + if (*callback != NULL) { + (*callback)(); + } +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/timer.h b/fastpair/internal/impl/windows/timer.h new file mode 100644 index 00000000..26ab8b77 --- /dev/null +++ b/fastpair/internal/impl/windows/timer.h @@ -0,0 +1,51 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_TIMER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_TIMER_H_ + +#include + +#include + +#include "fastpair/internal/api/timer.h" + +namespace location { +namespace nearby { +namespace windows { + +class Timer : public api::Timer { + public: + Timer() = default; + ~Timer() override; + + bool Create(int delay, int interval, std::function callback) override; + bool Stop() override; + bool FireNow() override; + + private: + static void CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired); + + int delay_; + int interval_; + std::function callback_; + HANDLE handle_ = NULL; + HANDLE timer_queue_handle_ = NULL; +}; + +} // namespace windows +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_TIMER_H_ diff --git a/fastpair/internal/impl/windows/timer_test.cc b/fastpair/internal/impl/windows/timer_test.cc new file mode 100644 index 00000000..f81523c6 --- /dev/null +++ b/fastpair/internal/impl/windows/timer_test.cc @@ -0,0 +1,69 @@ +// Copyright 2022 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 // NOLINT +#include // NOLINT +#include +#include // NOLINT + +#include "gtest/gtest.h" +#include "fastpair/internal/api/fast_pair_platform.h" + +namespace location { +namespace nearby { +namespace windows { +namespace { + +TEST(Timer, TestCreateTimer) { + int count = 0; + std::function callback = [&count]() { ++count; }; + + std::unique_ptr timer = + api::ImplementationFastPairPlatform::CreateTimer(); + + ASSERT_TRUE(timer != nullptr); + EXPECT_FALSE(timer->Create(-100, 0, callback)); + EXPECT_TRUE(timer->Stop()); +} + +// This test case cannot run on Google3 +TEST(Timer, DISABLED_TestRepeatTimer) { + int count = 0; + std::function callback = [&count]() { count++; }; + + std::unique_ptr timer = + api::ImplementationFastPairPlatform::CreateTimer(); + + ASSERT_TRUE(timer != nullptr); + EXPECT_TRUE(timer->Create(300, 300, callback)); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_TRUE(timer->Stop()); + EXPECT_EQ(count, 3); +} + +TEST(Timer, DISABLED_TestFireNow) { + int count = 0; + std::function callback = [&count]() { ++count; }; + auto timer = api::ImplementationFastPairPlatform::CreateTimer(); + EXPECT_TRUE(timer != nullptr); + EXPECT_TRUE(timer->Create(3000, 3000, callback)); + EXPECT_TRUE(timer->FireNow()); + EXPECT_TRUE(timer->Stop()); + EXPECT_EQ(count, 1); +} + +} // namespace +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/utils.cc b/fastpair/internal/impl/windows/utils.cc new file mode 100644 index 00000000..e90881ae --- /dev/null +++ b/fastpair/internal/impl/windows/utils.cc @@ -0,0 +1,57 @@ +// Copyright 2022 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 "fastpair/internal/impl/windows/utils.h" + +// Standard C/C++ headers +#include + +// Third party headers +#include "absl/strings/ascii.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" + +namespace location { +namespace nearby { +namespace windows { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress) { + std::string buffer = absl::StrFormat( + "%2llx:%2llx:%2llx:%2llx:%2llx:%2llx", 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) { + location::nearby::ByteArray mac_address_array = + location::nearby::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; +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/impl/windows/utils.h b/fastpair/internal/impl/windows/utils.h new file mode 100644 index 00000000..fc369a65 --- /dev/null +++ b/fastpair/internal/impl/windows/utils.h @@ -0,0 +1,39 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_UTILS_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_UTILS_H_ + +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "winrt/Windows.Foundation.h" +#include "winrt/base.h" + +namespace location { +namespace nearby { +namespace windows { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress); +uint64_t mac_address_string_to_uint64(absl::string_view mac_address); + +} // namespace windows +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_UTILS_H_ diff --git a/fastpair/internal/public/BUILD b/fastpair/internal/public/BUILD new file mode 100644 index 00000000..9ac346e7 --- /dev/null +++ b/fastpair/internal/public/BUILD @@ -0,0 +1,87 @@ +licenses(["notice"]) + +cc_library( + name = "types", + hdrs = [ + "device_info.h", + "task_runner.h", + "timer.h", + ], + visibility = ["//fastpair:__subpackages__"], + deps = [ + "//fastpair/internal/api:platform", + "//fastpair/internal/impl/g3:platform_g3", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + ], +) + +cc_library( + name = "fastpair_context", + srcs = [ + "task_runner_impl.cc", + "timer_impl.cc", + ], + hdrs = [ + "task_runner_impl.h", + "timer_impl.h", + ], + visibility = ["//fastpair:__subpackages__"], + deps = [ + ":device_info", + ":types", + "//fastpair/internal/api:platform", + "//fastpair/internal/base", # fixdeps: keep + "//fastpair/internal/impl/g3:platform_g3", + "//internal/platform:logging", + "//internal/platform:types", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/random", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + ], +) + +cc_library( + name = "device_info", + srcs = [ + "device_info_impl.cc", + ], + hdrs = [ + "device_info_impl.h", + ], + visibility = ["//fastpair:__subpackages__"], + deps = [ + ":types", + "//fastpair/internal/api:platform", + "//fastpair/internal/impl/g3:platform_g3", + ], +) + +cc_test( + name = "fastpair_context_test", + size = "small", + timeout = "short", + srcs = [ + "task_runner_impl_test.cc", + "timer_impl_test.cc", + ], + shard_count = 8, + deps = [ + "device_info", + ":fastpair_context", + ":types", + "//fastpair/internal/api:platform", + "//fastpair/internal/impl/g3:platform_g3", + "//internal/platform:logging", + "//internal/platform:types", + "//internal/platform/implementation/g3", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/internal/public/device_info.h b/fastpair/internal/public/device_info.h new file mode 100644 index 00000000..0696e8b1 --- /dev/null +++ b/fastpair/internal/public/device_info.h @@ -0,0 +1,47 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "fastpair/internal/api/device_info.h" +#include "fastpair/internal/api/fast_pair_platform.h" + +namespace location { +namespace nearby { +namespace fastpair { + +class DeviceInfo { + public: + virtual ~DeviceInfo() = default; + + virtual api::DeviceInfo::OsType GetOsType() const = 0; + + virtual bool IsScreenLocked() const = 0; + virtual void RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) = 0; + virtual void UnregisterScreenLockedListener( + absl::string_view listener_name) = 0; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_H_ diff --git a/fastpair/internal/public/device_info_impl.cc b/fastpair/internal/public/device_info_impl.cc new file mode 100644 index 00000000..e8dffa4f --- /dev/null +++ b/fastpair/internal/public/device_info_impl.cc @@ -0,0 +1,45 @@ +// Copyright 2022 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 "fastpair/internal/public/device_info_impl.h" + +#include +#include + +namespace location { +namespace nearby { +namespace fastpair { + +api::DeviceInfo::OsType DeviceInfoImpl::GetOsType() const { + return device_info_impl_->GetOsType(); +} + +bool DeviceInfoImpl::IsScreenLocked() const { + return device_info_impl_->IsScreenLocked(); +} + +void DeviceInfoImpl::RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) { + device_info_impl_->RegisterScreenLockedListener(listener_name, callback); +} + +void DeviceInfoImpl::UnregisterScreenLockedListener( + absl::string_view listener_name) { + device_info_impl_->UnregisterScreenLockedListener(listener_name); +} + +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/public/device_info_impl.h b/fastpair/internal/public/device_info_impl.h new file mode 100644 index 00000000..192d29e0 --- /dev/null +++ b/fastpair/internal/public/device_info_impl.h @@ -0,0 +1,51 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_IMPL_H_ + +#include +#include +#include + +#include "fastpair/internal/api/fast_pair_platform.h" +#include "fastpair/internal/public/device_info.h" + +namespace location { +namespace nearby { +namespace fastpair { + +class DeviceInfoImpl : public DeviceInfo { + public: + DeviceInfoImpl() + : device_info_impl_( + api::ImplementationFastPairPlatform::CreateDeviceInfo()) {} + + api::DeviceInfo::OsType GetOsType() 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; + + private: + std::unique_ptr device_info_impl_; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_DEVICE_INFO_IMPL_H_ diff --git a/fastpair/internal/public/task_runner.h b/fastpair/internal/public/task_runner.h new file mode 100644 index 00000000..502c7c6c --- /dev/null +++ b/fastpair/internal/public/task_runner.h @@ -0,0 +1,48 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_H_ + +#include + +#include "absl/time/time.h" + +namespace location { +namespace nearby { +namespace fastpair { + +// Task runner is an implementation to run tasks immediately or with a delay. +// The current implementation does not allow running nested tasks. +class TaskRunner { + public: + virtual ~TaskRunner() = default; + + // Posts a task to task runner. The task runs immediately or not depends on + // the implementation of class. If the implementation supports multiple + // threads, posted tasks could run concurrently. + virtual bool PostTask(std::function task) = 0; + + // Posts a task to run with delay. Multiple tasks can be scheduled. Tasks will + // execute in the order of their delay expiring, not in the order they were + // posted. + virtual bool PostDelayedTask(absl::Duration delay, + std::function task) = 0; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_H_ diff --git a/fastpair/internal/public/task_runner_impl.cc b/fastpair/internal/public/task_runner_impl.cc new file mode 100644 index 00000000..836eb70b --- /dev/null +++ b/fastpair/internal/public/task_runner_impl.cc @@ -0,0 +1,82 @@ +// Copyright 2022 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 "fastpair/internal/public/task_runner_impl.h" + +#include +#include +#include +#include +#include + +#include "absl/random/random.h" +#include "fastpair/internal/public/timer_impl.h" + +namespace location { +namespace nearby { +namespace fastpair { + +TaskRunnerImpl::TaskRunnerImpl(uint32_t runner_count) { + executor_ = + std::make_unique<::location::nearby::MultiThreadExecutor>(runner_count); +} + +TaskRunnerImpl::~TaskRunnerImpl() = default; + +bool TaskRunnerImpl::PostTask(std::function task) { + if (!task) { + return true; + } + + // Because of cannot get the executor status from platform API, just returns + // true after calling the Execute method. + executor_->Execute(std::move(task)); + return true; +} + +bool TaskRunnerImpl::PostDelayedTask(absl::Duration delay, + std::function task) { + if (!task) { + return true; + } + + absl::MutexLock lock(&mutex_); + uint64_t id = GenerateId(); + + std::unique_ptr timer = std::make_unique(); + if (timer->Start(delay / absl::Milliseconds(1), 0, + [this, id, task = std::move(task)]() { + if (task) { + PostTask(std::move(task)); + } + { + absl::MutexLock lock(&mutex_); + timers_map_.erase(id); + } + })) { + timers_map_.emplace(id, std::move(timer)); + return true; + } + + return false; +} + +uint64_t TaskRunnerImpl::GenerateId() { + absl::BitGen bitgen; + return absl::Uniform(bitgen, 0u, std::numeric_limits::max()); +} + +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/public/task_runner_impl.h b/fastpair/internal/public/task_runner_impl.h new file mode 100644 index 00000000..091cd0e5 --- /dev/null +++ b/fastpair/internal/public/task_runner_impl.h @@ -0,0 +1,56 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_IMPL_H_ + +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "fastpair/internal/public/task_runner.h" +#include "fastpair/internal/public/timer.h" +#include "internal/platform/multi_thread_executor.h" + +namespace location { +namespace nearby { +namespace fastpair { + +class TaskRunnerImpl : public TaskRunner { + public: + explicit TaskRunnerImpl(uint32_t runner_count); + ~TaskRunnerImpl() override; + + bool PostTask(std::function task) override; + bool PostDelayedTask(absl::Duration delay, + std::function task) override + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + uint64_t GenerateId(); + + mutable absl::Mutex mutex_; + std::unique_ptr<::location::nearby::MultiThreadExecutor> executor_; + absl::flat_hash_map> timers_map_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TASK_RUNNER_IMPL_H_ diff --git a/fastpair/internal/public/task_runner_impl_test.cc b/fastpair/internal/public/task_runner_impl_test.cc new file mode 100644 index 00000000..ba01b3e5 --- /dev/null +++ b/fastpair/internal/public/task_runner_impl_test.cc @@ -0,0 +1,166 @@ +// Copyright 2022 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 "fastpair/internal/public/task_runner_impl.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" + +namespace location { +namespace nearby { +namespace fastpair { +namespace { + +TEST(TaskRunnerImpl, PostTask) { + TaskRunnerImpl task_runner{1}; + absl::Notification notification; + bool called = false; + + task_runner.PostTask([&called, ¬ification]() { + called = true; + notification.Notify(); + }); + notification.WaitForNotificationWithTimeout(absl::Milliseconds(100)); + EXPECT_TRUE(called); +} + +TEST(TaskRunnerImpl, PostSequenceTasks) { + TaskRunnerImpl task_runner{1}; + std::vector completed_tasks; + absl::Notification notification; + + // Run the first task + task_runner.PostTask([&completed_tasks, ¬ification]() { + completed_tasks.push_back("task1"); + if (completed_tasks.size() == 2) { + absl::SleepFor(absl::Milliseconds(100)); + notification.Notify(); + } + }); + + // Run the second task + task_runner.PostTask([&completed_tasks, ¬ification]() { + completed_tasks.push_back("task2"); + if (completed_tasks.size() == 2) { + notification.Notify(); + } + }); + + notification.WaitForNotificationWithTimeout(absl::Milliseconds(200)); + ASSERT_EQ(completed_tasks.size(), 2u); + EXPECT_EQ(completed_tasks[0], "task1"); + EXPECT_EQ(completed_tasks[1], "task2"); +} + +TEST(TaskRunnerImpl, DISABLED_PostDelayedTask) { + TaskRunnerImpl task_runner{1}; + std::vector completed_tasks; + absl::Notification notification; + + // Run the first task + task_runner.PostDelayedTask(absl::Milliseconds(50), + [&completed_tasks, ¬ification]() { + completed_tasks.push_back("task1"); + if (completed_tasks.size() == 2) { + notification.Notify(); + } + }); + + // Run the second task + task_runner.PostTask([&completed_tasks, ¬ification]() { + completed_tasks.push_back("task2"); + if (completed_tasks.size() == 2) { + notification.Notify(); + } + }); + + notification.WaitForNotificationWithTimeout(absl::Milliseconds(200)); + ASSERT_EQ(completed_tasks.size(), 2u); + EXPECT_EQ(completed_tasks[0], "task2"); + EXPECT_EQ(completed_tasks[1], "task1"); +} + +TEST(TaskRunnerImpl, DISABLED_PostTwoDelayedTask) { + TaskRunnerImpl task_runner{1}; + std::vector completed_tasks; + absl::Notification notification; + + // Run the first task + task_runner.PostDelayedTask(absl::Milliseconds(100), + [&completed_tasks, ¬ification]() { + completed_tasks.push_back("task1"); + if (completed_tasks.size() == 2) { + notification.Notify(); + } + }); + + // Run the second task + task_runner.PostDelayedTask(absl::Milliseconds(50), + [&completed_tasks, ¬ification]() { + completed_tasks.push_back("task2"); + if (completed_tasks.size() == 2) { + notification.Notify(); + } + }); + + notification.WaitForNotificationWithTimeout(absl::Milliseconds(150)); + ASSERT_EQ(completed_tasks.size(), 2u); + EXPECT_EQ(completed_tasks[0], "task2"); + EXPECT_EQ(completed_tasks[1], "task1"); + + absl::Notification notification2; + task_runner.PostDelayedTask(absl::Milliseconds(100), + [&completed_tasks, ¬ification2]() { + completed_tasks.push_back("task3"); + notification2.Notify(); + }); + notification2.WaitForNotificationWithTimeout(absl::Milliseconds(150)); + ASSERT_EQ(completed_tasks.size(), 3u); + EXPECT_EQ(completed_tasks[2], "task3"); +} + +TEST(TaskRunnerImpl, PostTasksOnRunnerWithMultipleThreads) { + TaskRunnerImpl task_runner{10}; + std::atomic_int count = 0; + absl::Notification notification; + + for (int i = 0; i < 10; i++) { + task_runner.PostTask([&count, ¬ification]() { + absl::SleepFor(absl::Milliseconds(100)); + count++; + if (count == 10) { + notification.Notify(); + } + }); + } + + notification.WaitForNotificationWithTimeout(absl::Milliseconds(190)); + EXPECT_EQ(count, 10); +} + +TEST(TaskRunnerImpl, PostEmptyTask) { + TaskRunnerImpl task_runner{1}; + EXPECT_TRUE(task_runner.PostTask(nullptr)); + EXPECT_TRUE(task_runner.PostDelayedTask(absl::Milliseconds(100), nullptr)); +} + +} // namespace +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/public/timer.h b/fastpair/internal/public/timer.h new file mode 100644 index 00000000..6467b55a --- /dev/null +++ b/fastpair/internal/public/timer.h @@ -0,0 +1,51 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_H_ + +#include + +namespace location { +namespace nearby { +namespace fastpair { + +class Timer { + public: + virtual ~Timer() = default; + + // Starts the timer. + // + // @param delay + // The amount of time in milliseconds relative to the current time that must + // elapse before the timer is signaled for the first time. + // @param period + // The period of the timer, in milliseconds. + // If this parameter is zero, the timer is signaled once. + // If this parameter is greater than zero, the timer is periodic. + // @param callback + // The callback is called when timer is signaled + // @return + // Returns true if succeed, otherwise false is returned. + virtual bool Start(int delay, int period, std::function callback) = 0; + virtual bool Stop() = 0; + virtual bool IsRunning() = 0; + virtual bool FireNow() = 0; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_H_ diff --git a/fastpair/internal/public/timer_impl.cc b/fastpair/internal/public/timer_impl.cc new file mode 100644 index 00000000..f7aa1864 --- /dev/null +++ b/fastpair/internal/public/timer_impl.cc @@ -0,0 +1,68 @@ +// Copyright 2022 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 "fastpair/internal/public/timer_impl.h" + +#include +#include + +#include "absl/time/clock.h" +#include "internal/platform/logging.h" + +namespace location { +namespace nearby { +namespace fastpair { + +bool TimerImpl::Start(int delay, int period, std::function callback) { + if (internal_timer_ != nullptr) { + NEARBY_LOGS(INFO) << "The timer is already running."; + return false; + } + + delay_ = delay; + period_ = period; + callback_ = std::move(callback); + internal_timer_ = api::ImplementationFastPairPlatform::CreateTimer(); + if (!internal_timer_->Create(delay, period, callback_)) { + NEARBY_LOGS(INFO) << "Failed to create timer."; + internal_timer_ = nullptr; + return false; + } + return true; +} + +bool TimerImpl::Stop() { + if (internal_timer_ == nullptr) { + return true; + } + if (internal_timer_->Stop()) { + internal_timer_ = nullptr; + return true; + } + return false; +} + +bool TimerImpl::IsRunning() { return (internal_timer_ != nullptr); } + +bool TimerImpl::FireNow() { + if (IsRunning()) { + return internal_timer_->FireNow(); + } + + return false; +} + +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/fastpair/internal/public/timer_impl.h b/fastpair/internal/public/timer_impl.h new file mode 100644 index 00000000..b2b7f580 --- /dev/null +++ b/fastpair/internal/public/timer_impl.h @@ -0,0 +1,48 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_IMPL_H_ + +#include +#include + +#include "fastpair/internal/api/fast_pair_platform.h" +#include "fastpair/internal/public/timer.h" + +namespace location { +namespace nearby { +namespace fastpair { + +class TimerImpl : public Timer { + public: + ~TimerImpl() override { Stop(); } + + bool Start(int delay, int period, std::function callback) override; + bool Stop() override; + bool IsRunning() override; + bool FireNow() override; + + private: + int delay_ = 0; + int period_ = 0; + std::function callback_ = nullptr; + std::unique_ptr internal_timer_ = nullptr; +}; + +} // namespace fastpair +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_PUBLIC_TIMER_IMPL_H_ diff --git a/fastpair/internal/public/timer_impl_test.cc b/fastpair/internal/public/timer_impl_test.cc new file mode 100644 index 00000000..c8e6ac27 --- /dev/null +++ b/fastpair/internal/public/timer_impl_test.cc @@ -0,0 +1,62 @@ +// Copyright 2022 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 "fastpair/internal/public/timer_impl.h" + +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace fastpair { +namespace { + +TEST(TimerImpl, TestCreateTimer) { + TimerImpl timer; + + EXPECT_FALSE(timer.Start(-100, 0, nullptr)); + EXPECT_TRUE(timer.Start(100, 100, []() {})); + EXPECT_TRUE(timer.Stop()); +} + +TEST(TimerImpl, TestRunningStatus) { + TimerImpl timer; + + EXPECT_TRUE(timer.Start(100, 100, []() {})); + EXPECT_TRUE(timer.IsRunning()); + EXPECT_TRUE(timer.Stop()); + EXPECT_FALSE(timer.IsRunning()); +} + +TEST(TimerImpl, TestStartRunningTimer) { + TimerImpl timer; + + EXPECT_TRUE(timer.Start(100, 100, []() {})); + EXPECT_FALSE(timer.Start(100, 100, []() {})); + EXPECT_TRUE(timer.Stop()); +} + +TEST(TimerImpl, TestFireNow) { + TimerImpl timer; + int count = 0; + + EXPECT_TRUE(timer.Start(100, 100, [&count]() { ++count; })); + EXPECT_TRUE(timer.FireNow()); + EXPECT_TRUE(timer.Stop()); + EXPECT_EQ(count, 1); +} + +} // namespace +} // namespace fastpair +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index ffc08e74..1984b459 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -148,6 +148,7 @@ cc_library( defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"], visibility = [ "//connections/clients/windows:__subpackages__", + "//fastpair/internal/impl/windows:__subpackages__", "//location/nearby:__subpackages__", ], deps = [