diff --git a/presence/BUILD b/presence/BUILD index d59bd33a..a34e26ea 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -39,6 +39,7 @@ cc_library( "presence_client.cc", "presence_device.cc", "presence_zone.cc", + "scan_request_builder.cc", ], hdrs = [ "broadcast_options.h", @@ -53,10 +54,12 @@ cc_library( "presence_device.h", "presence_zone.h", "scan_request.h", + "scan_request_builder.h", "status.h", ], deps = [ ":credential", + "//net/proto2/util/public:differencer", "//internal/platform:base", "//third_party/nearby/presence/proto:credential_cc_proto", "@com_google_absl//absl/strings", @@ -219,6 +222,7 @@ cc_test( "presence_device_test.cc", "presence_identity_test.cc", "presence_zone_test.cc", + "scan_request_builder_test.cc", "status_test.cc", ], shard_count = 6, diff --git a/presence/scan_request.h b/presence/scan_request.h index ead93618..85f302fd 100644 --- a/presence/scan_request.h +++ b/presence/scan_request.h @@ -18,14 +18,20 @@ #include #include +#include "net/proto2/util/public/message_differencer.h" #include "third_party/nearby/presence/data_element.h" #include "third_party/nearby/presence/power_mode.h" #include "third_party/nearby/presence/presence_identity.h" #include "third_party/nearby/presence/proto/credential.pb.h" +using MD = proto2::util::MessageDifferencer; + namespace nearby { namespace presence { +constexpr char kPresenceScanFilterName[] = "PresenceScanFilter"; +constexpr char kLegacyPresenceScanFilterName[] = "LegacyPresenceScanFilter"; + enum class ScanType { kUnspecifiedScan = 0, kFastPairScan = 1, @@ -72,6 +78,36 @@ struct LegacyPresenceScanFilter : public ScanFilter { std::vector extended_properties; }; +inline bool operator==(const ScanFilter& a, const ScanFilter& b) { + if (typeid(a) != typeid(b)) return false; + if (strcmp(typeid(a).name(), kPresenceScanFilterName) == 0) { + const PresenceScanFilter a_p = static_cast(a); + const PresenceScanFilter b_p = static_cast(b); + if (a_p.extended_properties != b_p.extended_properties) return false; + } else if (strcmp(typeid(a).name(), kLegacyPresenceScanFilterName) == 0) { + const LegacyPresenceScanFilter a_l = + static_cast(a); + const LegacyPresenceScanFilter b_l = + static_cast(b); + if (a_l.path_loss_threshold != b_l.path_loss_threshold || + a_l.actions != b_l.actions || + a_l.remote_public_credentials.size() != + b_l.remote_public_credentials.size() || + a_l.extended_properties != b_l.extended_properties) + return false; + for (int i = 0; i < a_l.remote_public_credentials.size(); ++i) { + if (!MD::Equivalent(a_l.remote_public_credentials[i], + b_l.remote_public_credentials[i])) + return false; + } + } + return a.scan_type == b.scan_type; +} + +inline bool operator!=(const ScanFilter& a, const ScanFilter& b) { + return !(a == b); +} + /** * An encapsulation of various parameters for requesting nearby scans. */ @@ -96,6 +132,19 @@ struct ScanRequest { bool scan_only_when_screen_on; }; +inline bool operator==(const ScanRequest& a, const ScanRequest& b) { + if (a.identity_types != b.identity_types) return false; + if (a.scan_filters != b.scan_filters) return false; + return a.scan_only_when_screen_on == b.scan_only_when_screen_on && + a.power_mode == b.power_mode && a.scan_type == b.scan_type && + a.use_ble == b.use_ble && a.account_name == b.account_name && + b.identity_types == b.identity_types; +} + +inline bool operator!=(const ScanRequest& a, const ScanRequest& b) { + return !(a == b); +} + } // namespace presence } // namespace nearby diff --git a/presence/scan_request_builder.cc b/presence/scan_request_builder.cc new file mode 100644 index 00000000..f5a2692e --- /dev/null +++ b/presence/scan_request_builder.cc @@ -0,0 +1,77 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include "third_party/nearby/presence/scan_request.h" +#include "third_party/nearby/presence/scan_request_builder.h" + +namespace nearby { +namespace presence { + +ScanRequestBuilder& ScanRequestBuilder::SetAccountName( + absl::string_view account_name) { + request_.account_name = account_name; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetPowerMode(PowerMode power_mode) { + request_.power_mode = power_mode; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetScanType(ScanType scan_type) { + request_.scan_type = scan_type; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::AddIdentityType( + PresenceIdentity::IdentityType identity_type) { + request_.identity_types.push_back(identity_type); + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetIdentityTypes( + std::vector types) { + request_.identity_types = types; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::AddScanFilter(ScanFilter scan_filter) { + request_.scan_filters.push_back(scan_filter); + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetScanFilters( + std::vector filters) { + request_.scan_filters = filters; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetUseBle(bool use_ble) { + request_.use_ble = use_ble; + return *this; +} + +ScanRequestBuilder& ScanRequestBuilder::SetOnlyScreenOnScan( + bool screen_on_only_scan) { + request_.scan_only_when_screen_on = screen_on_only_scan; + return *this; +} + +ScanRequest ScanRequestBuilder::Build() { + return this->request_; +} + +} // namespace presence +} // namespace nearby diff --git a/presence/scan_request_builder.h b/presence/scan_request_builder.h new file mode 100644 index 00000000..1ecf5012 --- /dev/null +++ b/presence/scan_request_builder.h @@ -0,0 +1,52 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ +#define THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ + +#include + +#include "absl/strings/string_view.h" +#include "third_party/nearby/presence/power_mode.h" +#include "third_party/nearby/presence/presence_identity.h" +#include "third_party/nearby/presence/presence_zone.h" +#include "third_party/nearby/presence/scan_request.h" + +namespace nearby { +namespace presence { +class ScanRequestBuilder { + private: + ScanRequest request_; + + public: + ScanRequestBuilder& SetAccountName(absl::string_view account_name); + ScanRequestBuilder& SetPowerMode(PowerMode power_mode); + ScanRequestBuilder& SetScanType(ScanType scan_type); + ScanRequestBuilder& AddIdentityType( + PresenceIdentity::IdentityType identity_type); + ScanRequestBuilder& SetIdentityTypes( + std::vector types); + ScanRequestBuilder& AddScanFilter(ScanFilter scan_filter); + ScanRequestBuilder& SetScanFilters(std::vector filters); + ScanRequestBuilder& SetUseBle(bool use_ble); + ScanRequestBuilder& SetOnlyScreenOnScan(bool screen_on_only_scan); + ScanRequest Build(); + inline bool operator==(const ScanRequestBuilder& other) const { + return request_ == other.request_; + } +}; +} // namespace presence +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ diff --git a/presence/scan_request_builder_test.cc b/presence/scan_request_builder_test.cc new file mode 100644 index 00000000..689ece2a --- /dev/null +++ b/presence/scan_request_builder_test.cc @@ -0,0 +1,152 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "third_party/nearby/presence/scan_request_builder.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "third_party/nearby/presence/power_mode.h" +#include "third_party/nearby/presence/presence_identity.h" +#include "third_party/nearby/presence/scan_request.h" + +namespace nearby { +namespace presence { + +constexpr char kAccountName[] = "Google User"; +constexpr bool kUseBle = true; +constexpr bool kOnlyScreenOnScan = true; +const PresenceIdentity::IdentityType kIdentity = + PresenceIdentity::IdentityType::kPrivate; +const ScanType kScanType = ScanType::kPresenceScan; +const PowerMode kPowerMode = PowerMode::kLowLatency; + +std::vector MockFilters() { + const LegacyPresenceScanFilter legacyFilter{{.scan_type = kScanType}}; + const PresenceScanFilter newFilter{{.scan_type = kScanType}}; + std::vector filters = {legacyFilter, newFilter}; + return filters; +} + +TEST(ScanRequestBuilderTest, TestConstructor) { + EXPECT_FALSE(std::is_trivially_constructible::value); +} + +TEST(ScanRequestBuilderTest, TestSetAccountName) { + ScanRequestBuilder builder; + builder.SetAccountName(kAccountName); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.account_name, kAccountName); +} + +TEST(ScanRequestBuilderTest, TestSetPowerMode) { + ScanRequestBuilder builder; + builder.SetPowerMode(kPowerMode); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.power_mode, kPowerMode); +} + +TEST(ScanRequestBuilderTest, TestSetScanType) { + ScanRequestBuilder builder; + builder.SetScanType(kScanType); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.scan_type, kScanType); +} + +TEST(ScanRequestBuilderTest, TestAddIdentityType) { + ScanRequestBuilder builder; + builder.AddIdentityType(kIdentity); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.identity_types.size(), 1); + EXPECT_EQ(sr.identity_types[0], kIdentity); +} + +TEST(ScanRequestBuilderTest, TestSetIdentityTypes) { + ScanRequestBuilder builder; + std::vector types = {kIdentity}; + builder.SetIdentityTypes(types); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.identity_types.size(), 1); + EXPECT_EQ(sr.identity_types, types); +} + +TEST(ScanRequestBuilderTest, TestAddScanFilter) { + ScanRequestBuilder builder; + LegacyPresenceScanFilter legacyFilter = {}; + legacyFilter.scan_type = kScanType; + builder.AddScanFilter(legacyFilter); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.scan_filters.size(), 1); + EXPECT_EQ(sr.scan_filters[0].scan_type, kScanType); +} + +TEST(ScanRequestBuilderTest, TestSetScanFilters) { + ScanRequestBuilder builder; + std::vector filters = MockFilters(); + builder.SetScanFilters(filters); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.scan_filters.size(), 2); + EXPECT_EQ(sr.scan_filters[0].scan_type, kScanType); + EXPECT_EQ(sr.scan_filters[1].scan_type, kScanType); +} + +TEST(ScanRequestBuilderTest, TestNotEqualScanFilter) { + std::vector filters = MockFilters(); + ScanRequestBuilder builderLegacy, builderModern; + ScanRequest legacy = builderLegacy.AddScanFilter(filters[0]).Build(); + ScanRequest modern = builderModern.AddScanFilter(filters[1]).Build(); + EXPECT_NE(legacy, modern); +} + +TEST(ScanRequestBuilderTest, TestSetUseBle) { + ScanRequestBuilder builder; + builder.SetUseBle(kUseBle); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.use_ble, kUseBle); +} + +TEST(ScanRequestBuilderTest, TestSetOnlyScreenOnScan) { + ScanRequestBuilder builder; + builder.SetOnlyScreenOnScan(kOnlyScreenOnScan); + ScanRequest sr = builder.Build(); + EXPECT_EQ(sr.scan_only_when_screen_on, kOnlyScreenOnScan); +} + +TEST(ScanRequestBuilderTest, TestChainCalls) { + ScanRequestBuilder builder; + ScanRequest sr = builder.SetAccountName(kAccountName) + .SetPowerMode(kPowerMode) + .SetOnlyScreenOnScan(kOnlyScreenOnScan) + .SetUseBle(kUseBle) + .Build(); + EXPECT_EQ(sr.account_name, kAccountName); + EXPECT_EQ(sr.scan_only_when_screen_on, kOnlyScreenOnScan); + EXPECT_EQ(sr.power_mode, kPowerMode); + EXPECT_EQ(sr.use_ble, kUseBle); +} + +TEST(ScanRequestBuilderTest, TestCopy) { + ScanRequestBuilder builder1; + builder1.SetOnlyScreenOnScan(kOnlyScreenOnScan).SetUseBle(kUseBle); + ScanRequestBuilder builder2 = {builder1}; + EXPECT_EQ(builder1, builder2); + ScanRequest s1 = builder1.Build(); + ScanRequest s2 = builder2.Build(); + EXPECT_EQ(s1, s2); +} + +} // namespace presence +} // namespace nearby