From 99053e4df375225ca18f1ab7b45a4615029f6c11 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Mon, 17 Jul 2023 17:19:00 -0700 Subject: [PATCH] Implement scanner for fast pair subsequent pairing PiperOrigin-RevId: 548841808 --- fastpair/common/fast_pair_device.h | 9 + fastpair/keyed_service/fast_pair_mediator.cc | 4 + .../repository/fake_fast_pair_repository.cc | 23 +- .../repository/fake_fast_pair_repository.h | 12 +- fastpair/scanning/BUILD | 1 + fastpair/scanning/fastpair/BUILD | 21 ++ .../fast_pair_non_discoverable_scanner.cc | 194 ++++++++++++ .../fast_pair_non_discoverable_scanner.h | 104 ++++++ ...fast_pair_non_discoverable_scanner_test.cc | 298 ++++++++++++++++++ fastpair/scanning/scanner_broker_impl.cc | 10 + fastpair/scanning/scanner_broker_impl.h | 3 + fastpair/scanning/scanner_broker_impl_test.cc | 66 +++- 12 files changed, 738 insertions(+), 7 deletions(-) create mode 100644 fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc create mode 100644 fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h create mode 100644 fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner_test.cc diff --git a/fastpair/common/fast_pair_device.h b/fastpair/common/fast_pair_device.h index b43aecc8..be02d25f 100644 --- a/fastpair/common/fast_pair_device.h +++ b/fastpair/common/fast_pair_device.h @@ -97,6 +97,14 @@ class FastPairDevice { return ble_address_; } + void SetShowUiNotification(bool should_show_ui_notification) { + should_show_ui_notification_ = should_show_ui_notification; + } + + std::optional ShouldShowUiNotification() const { + return should_show_ui_notification_; + } + private: std::string model_id_; @@ -126,6 +134,7 @@ class FastPairDevice { AccountKey account_key_; std::optional metadata_; + std::optional should_show_ui_notification_; }; std::ostream& operator<<(std::ostream& stream, const FastPairDevice& device); diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index da17b826..44913753 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -94,6 +94,10 @@ Mediator::Mediator( void Mediator::OnDeviceFound(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; + if (device.ShouldShowUiNotification().value_or(false)) { + NEARBY_LOGS(INFO) << __func__ << ": Ignoring because show UI flag is false"; + return; + } if (IsDeviceCurrentlyShowingNotification(device)) { NEARBY_LOGS(VERBOSE) << __func__ << ": Extending notification for re-discovered device=" diff --git a/fastpair/repository/fake_fast_pair_repository.cc b/fastpair/repository/fake_fast_pair_repository.cc index eafe6696..98329e3e 100644 --- a/fastpair/repository/fake_fast_pair_repository.cc +++ b/fastpair/repository/fake_fast_pair_repository.cc @@ -15,11 +15,13 @@ #include "fastpair/repository/fake_fast_pair_repository.h" #include +#include #include #include #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" +#include "fastpair/common/account_key.h" #include "fastpair/common/constant.h" #include "fastpair/proto/fastpair_rpcs.proto.h" @@ -36,16 +38,32 @@ void FakeFastPairRepository::ClearFakeMetadata(absl::string_view hex_model_id) { data_.erase(hex_model_id); } +void FakeFastPairRepository::SetResultOfCheckIfAssociatedWithCurrentAccount( + std::optional account_key, + std::optional model_id) { + account_key_ = std::move(account_key); + model_id_ = std::move(model_id); +} + void FakeFastPairRepository::GetDeviceMetadata( absl::string_view hex_model_id, DeviceMetadataCallback callback) { - executor_.Execute([callback = std::move(callback), this, + executor_.Execute([this, callback = std::move(callback), hex_model_id = std::string(hex_model_id)]() mutable { if (data_.contains(hex_model_id)) { - callback(*data_[hex_model_id]); + std::move(callback)(*data_[hex_model_id]); + } else { + std::move(callback)(std::nullopt); } }); } +void FakeFastPairRepository::CheckIfAssociatedWithCurrentAccount( + AccountKeyFilter& account_key_filter, CheckAccountKeysCallback callback) { + executor_.Execute([this, callback = std::move(callback)]() mutable { + std::move(callback)(account_key_, model_id_); + }); +} + std::unique_ptr FakeFastPairRepository::Create( absl::string_view model_id, absl::string_view public_anti_spoof_key) { proto::Device metadata; @@ -64,6 +82,5 @@ std::unique_ptr FakeFastPairRepository::Create( repository->SetFakeMetadata(model_id, metadata); return repository; } - } // namespace fastpair } // namespace nearby diff --git a/fastpair/repository/fake_fast_pair_repository.h b/fastpair/repository/fake_fast_pair_repository.h index 93016fbd..801ab80b 100644 --- a/fastpair/repository/fake_fast_pair_repository.h +++ b/fastpair/repository/fake_fast_pair_repository.h @@ -17,9 +17,11 @@ #include #include +#include #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "fastpair/common/account_key.h" #include "fastpair/common/device_metadata.h" #include "fastpair/repository/fast_pair_repository.h" #include "internal/platform/single_thread_executor.h" @@ -39,6 +41,10 @@ class FakeFastPairRepository : public FastPairRepository { void SetFakeMetadata(absl::string_view hex_model_id, proto::Device metadata); void ClearFakeMetadata(absl::string_view hex_model_id); + void SetResultOfCheckIfAssociatedWithCurrentAccount( + std::optional account_key, + std::optional model_id); + // FastPairRepository:: void AddObserver(Observer* observer) override{}; void RemoveObserver(Observer* observer) override{}; @@ -58,10 +64,14 @@ class FakeFastPairRepository : public FastPairRepository { void CheckIfAssociatedWithCurrentAccount( AccountKeyFilter& account_key_filter, - CheckAccountKeysCallback callback) override{}; + CheckAccountKeysCallback callback) override; private: absl::flat_hash_map> data_; + + // Results of CheckIfAssociatedWithCurrentAccount + std::optional account_key_; + std::optional model_id_; SingleThreadExecutor executor_; }; } // namespace fastpair diff --git a/fastpair/scanning/BUILD b/fastpair/scanning/BUILD index 4b665194..521dd002 100644 --- a/fastpair/scanning/BUILD +++ b/fastpair/scanning/BUILD @@ -69,6 +69,7 @@ cc_test( "//fastpair/internal/mediums", "//fastpair/proto:fastpair_cc_proto", "//fastpair/repository:test_support", + "//fastpair/testing", "//internal/platform:base", "//internal/platform:test_util", "//internal/platform:types", diff --git a/fastpair/scanning/fastpair/BUILD b/fastpair/scanning/fastpair/BUILD index 5c5bc293..f5e218b6 100644 --- a/fastpair/scanning/fastpair/BUILD +++ b/fastpair/scanning/fastpair/BUILD @@ -18,11 +18,13 @@ cc_library( name = "scanning", srcs = [ "fast_pair_discoverable_scanner_impl.cc", + "fast_pair_non_discoverable_scanner.cc", "fast_pair_scanner_impl.cc", ], hdrs = [ "fast_pair_discoverable_scanner.h", "fast_pair_discoverable_scanner_impl.h", + "fast_pair_non_discoverable_scanner.h", "fast_pair_scanner.h", "fast_pair_scanner_impl.h", ], @@ -112,3 +114,22 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "fast_pair_non_discoverable_scanner_test", + size = "small", + srcs = [ + "fast_pair_non_discoverable_scanner_test.cc", + ], + shard_count = 16, + deps = [ + ":scanning", + ":test_support", + "//fastpair/common", + "//fastpair/repository:test_support", + "//fastpair/testing", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc new file mode 100644 index 00000000..43148584 --- /dev/null +++ b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc @@ -0,0 +1,194 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h" + +#include +#include +#include +#include +#include + +#include "fastpair/common/constant.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/non_discoverable_advertisement.h" +#include "fastpair/dataparser/fast_pair_data_parser.h" +#include "fastpair/repository/fast_pair_repository.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace fastpair { + +// static +FastPairNonDiscoverableScanner::Factory* + FastPairNonDiscoverableScanner::Factory::g_test_factory_ = nullptr; + +// static +std::unique_ptr +FastPairNonDiscoverableScanner::Factory::Create( + FastPairScanner& scanner, NonDiscoverableScannerCallback found_callback, + NonDiscoverableScannerCallback lost_callback, + SingleThreadExecutor* executor, + FastPairDeviceRepository* device_repository) { + if (g_test_factory_) { + return g_test_factory_->CreateInstance(scanner, std::move(found_callback), + std::move(lost_callback), executor, + device_repository); + } + + return std::make_unique( + scanner, std::move(found_callback), std::move(lost_callback), executor, + device_repository); +} + +// static +void FastPairNonDiscoverableScanner::Factory::SetFactoryForTesting( + Factory* g_test_factory) { + g_test_factory_ = g_test_factory; +} + +FastPairNonDiscoverableScanner::FastPairNonDiscoverableScanner( + FastPairScanner& scanner, NonDiscoverableScannerCallback found_callback, + NonDiscoverableScannerCallback lost_callback, + SingleThreadExecutor* executor, FastPairDeviceRepository* device_repository) + : scanner_(scanner), + found_callback_(std::move(found_callback)), + lost_callback_(std::move(lost_callback)), + executor_(executor), + device_repository_(device_repository) { + scanner_.AddObserver(this); +} + +FastPairNonDiscoverableScanner::~FastPairNonDiscoverableScanner() { + scanner_.RemoveObserver(this); +} + +void FastPairNonDiscoverableScanner::OnDeviceFound( + const BlePeripheral& peripheral) { + std::string fast_pair_service_data = + peripheral.GetAdvertisementBytes(kServiceId).string_data(); + if (fast_pair_service_data.empty()) { + NEARBY_LOGS(WARNING) << __func__ + << ": Device doesn't have any Fast Pair Service Data."; + return; + } + executor_->Execute( + "device-found", + [this, fast_pair_service_data = std::move(fast_pair_service_data), + address = + peripheral.GetName()]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + NEARBY_LOGS(INFO) << __func__ << ": Attempting to parse advertisement."; + FastPairDataParser::ParseNotDiscoverableAdvertisement( + fast_pair_service_data, address, + [&](std::optional advertisement) { + OnAdvertisementParsed(address, advertisement); + }); + }); +} + +void FastPairNonDiscoverableScanner::OnAdvertisementParsed( + absl::string_view address, + std::optional advertisement) { + if (!advertisement.has_value()) { + NEARBY_LOGS(INFO) + << __func__ << ": Returning early because no advertisement was parsed."; + return; + } + NEARBY_LOGS(INFO) + << __func__ + << ": Attempting to check if device is associated with current account."; + AccountKeyFilter account_filter(advertisement.value()); + FastPairRepository::Get()->CheckIfAssociatedWithCurrentAccount( + account_filter, [&, address = std::string(address), + advertisement = std::move(advertisement)]( + std::optional account_key, + std::optional model_id) { + OnAccountKeyFilterCheckResult(address, advertisement, account_key, + model_id); + }); +} + +void FastPairNonDiscoverableScanner::OnAccountKeyFilterCheckResult( + absl::string_view address, + std::optional advertisement, + std::optional account_key, + std::optional model_id) { + if (!account_key.has_value() || !model_id.has_value()) { + return; + } + NEARBY_LOGS(INFO) << __func__ << ": Attempting to get device metadata."; + FastPairRepository::Get()->GetDeviceMetadata( + model_id.value(), + [&, address = std::string(address), + advertisement = std::move(advertisement), model_id = model_id.value(), + account_key = + account_key.value()](std::optional device_metadata) { + OnDeviceMetadataRetrieved(address, advertisement, model_id, account_key, + device_metadata); + }); +} + +void FastPairNonDiscoverableScanner::OnDeviceMetadataRetrieved( + absl::string_view address, + std::optional advertisement, + absl::string_view model_id, AccountKey account_key, + std::optional device_metadata) { + if (!device_metadata.has_value()) { + NEARBY_LOGS(WARNING) << __func__ << ": Failed to get device metadata"; + return; + } + + auto fast_pair_device = std::make_unique( + model_id, address, Protocol::kFastPairSubsequentPairing); + fast_pair_device->SetAccountKey(account_key); + fast_pair_device->SetMetadata(device_metadata.value()); + fast_pair_device->SetShowUiNotification( + advertisement->type == NonDiscoverableAdvertisement::Type::kShowUi); + executor_->Execute( + "add-device", + [this, fast_pair_device = std::move(fast_pair_device)]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable { + FastPairDevice* device = + device_repository_->AddDevice(std::move(fast_pair_device)); + NotifyDeviceFound(*device); + }); +} + +void FastPairNonDiscoverableScanner::NotifyDeviceFound(FastPairDevice& device) { + NEARBY_LOGS(VERBOSE) << "Notify Device found:" + << "BluetoothAddress = " << device.GetBleAddress() + << ", Model id = " << device.GetModelId(); + found_callback_(device); +} + +void FastPairNonDiscoverableScanner::OnDeviceLost( + const BlePeripheral& peripheral) { + NEARBY_LOGS(INFO) << __func__ << ": Running lost callback"; + executor_->Execute("device-lost", + [this, address = peripheral.GetName()]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + auto opt_device = + device_repository_->FindDevice(address); + + // Don't invoke callback if we didn't notify this + // device. + if (!opt_device.has_value()) return; + FastPairDevice* device = opt_device.value(); + lost_callback_(*device); + device_repository_->RemoveDevice(device); + }); +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h new file mode 100644 index 00000000..aa38e825 --- /dev/null +++ b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h @@ -0,0 +1,104 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_FASTPAIR_FAST_PAIR_NON_DISCOVERABLE_SCANNER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_FASTPAIR_FAST_PAIR_NON_DISCOVERABLE_SCANNER_H_ + +#include +#include + +#include "absl/functional/any_invocable.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/non_discoverable_advertisement.h" +#include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/scanning/fastpair/fast_pair_scanner.h" +#include "internal/base/observer_list.h" +#include "internal/platform/single_thread_executor.h" + +namespace nearby { +namespace fastpair { +using NonDiscoverableScannerCallback = + absl::AnyInvocable; +// This class detects Fast Pair 'not discoverable' advertisements (see +// https://developers.google.com/nearby/fast-pair/spec#AdvertisingWhenNotDiscoverable) +// and invokes the |found_callback| when it finds a device within the +// appropriate range. |lost_callback| will be invoked when that device is lost. +class FastPairNonDiscoverableScanner : public FastPairScanner::Observer { + public: + class Factory { + public: + static std::unique_ptr Create( + FastPairScanner& scanner, NonDiscoverableScannerCallback found_callback, + NonDiscoverableScannerCallback lost_callback, + SingleThreadExecutor* executor, + FastPairDeviceRepository* device_repository); + + static void SetFactoryForTesting(Factory* g_test_factory); + + protected: + virtual ~Factory() = default; + virtual std::unique_ptr CreateInstance( + FastPairScanner& scanner, NonDiscoverableScannerCallback found_callback, + NonDiscoverableScannerCallback lost_callback, + SingleThreadExecutor* executor, + FastPairDeviceRepository* device_repository) = 0; + + private: + static Factory* g_test_factory_; + }; + + FastPairNonDiscoverableScanner(FastPairScanner& scanner, + NonDiscoverableScannerCallback found_callback, + NonDiscoverableScannerCallback lost_callback, + SingleThreadExecutor* executor, + FastPairDeviceRepository* device_repository); + FastPairNonDiscoverableScanner(const FastPairNonDiscoverableScanner&) = + delete; + FastPairNonDiscoverableScanner& operator=( + const FastPairNonDiscoverableScanner&) = delete; + ~FastPairNonDiscoverableScanner(); + + // FastPairScanner::Observer + void OnDeviceFound(const BlePeripheral& peripheral) override; + void OnDeviceLost(const BlePeripheral& peripheral) override; + + private: + void OnAdvertisementParsed( + absl::string_view address, + std::optional advertisement); + void OnAccountKeyFilterCheckResult( + absl::string_view address, + std::optional advertisement, + std::optional account_key, + std::optional model_id); + void OnDeviceMetadataRetrieved( + absl::string_view address, + std::optional advertisement, + absl::string_view model_id, AccountKey account_key, + std::optional device_metadata); + void NotifyDeviceFound(FastPairDevice& device) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + + FastPairScanner& scanner_; + NonDiscoverableScannerCallback found_callback_ ABSL_GUARDED_BY(*executor_); + NonDiscoverableScannerCallback lost_callback_ ABSL_GUARDED_BY(*executor_); + SingleThreadExecutor* executor_; + FastPairDeviceRepository* device_repository_ ABSL_GUARDED_BY(*executor_); + ObserverList observer_list_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_FASTPAIR_FAST_PAIR_NON_DISCOVERABLE_SCANNER_H_ diff --git a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner_test.cc b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner_test.cc new file mode 100644 index 00000000..8b5f9312 --- /dev/null +++ b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner_test.cc @@ -0,0 +1,298 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "fastpair/common/account_key.h" +#include "fastpair/repository/fake_fast_pair_repository.h" +#include "fastpair/scanning/fastpair/fake_fast_pair_scanner.h" +#include "fastpair/testing/fast_pair_service_data_creator.h" + +namespace nearby { +namespace fastpair { +namespace { +constexpr int kNotDiscoverableAdvHeader = 0b00000110; +constexpr int kAccountKeyFilterHeader = 0b01100000; +constexpr int kAccountKeyFilterNoNotificationHeader = 0b01100010; +constexpr int kSaltHeader = 0b00010001; +constexpr absl::string_view kModelId("aabbcc"); +constexpr absl::string_view kBleAddress("11:12:13:14:15:16"); +constexpr absl::string_view kAccountKeyFilter("112233445566"); +constexpr absl::string_view kSalt("01"); + +// Short timeout for operation that we expect to timeout. +constexpr absl::Duration kFailureTimeout = absl::Milliseconds(100); + +class FakeBlePeripheral : public api::BlePeripheral { + public: + explicit FakeBlePeripheral(absl::string_view name, + std::vector service_data) { + name_ = std::string(name); + ByteArray advertisement_bytes( + std::string(service_data.begin(), service_data.end())); + advertisement_data_ = advertisement_bytes; + } + + FakeBlePeripheral(const FakeBlePeripheral&) = default; + ~FakeBlePeripheral() override = default; + + std::string GetName() const override { return name_; } + + ByteArray GetAdvertisementBytes( + const std::string& service_id) const override { + return advertisement_data_; + } + + void SetName(const std::string& name) { name_ = name; } + + void SetAdvertisementBytes(ByteArray advertisement_bytes) { + advertisement_data_ = advertisement_bytes; + } + + private: + std::string name_; + ByteArray advertisement_data_; +}; + +class FastPairNonDiscoverableScannerTest : public ::testing::Test { + protected: + void TearDown() override { + executor_.Shutdown(); + } + + std::vector GetAdvServicedata() { + return FastPairServiceDataCreator::Builder() + .SetHeader(kNotDiscoverableAdvHeader) + .SetModelId(kModelId) + .AddExtraFieldHeader(kAccountKeyFilterHeader) + .AddExtraField(kAccountKeyFilter) + .AddExtraFieldHeader(kSaltHeader) + .AddExtraField(kSalt) + .Build() + ->CreateServiceData(); + } + + std::vector GetAdvNoUiServicedata() { + return FastPairServiceDataCreator::Builder() + .SetHeader(kNotDiscoverableAdvHeader) + .SetModelId(kModelId) + .AddExtraFieldHeader(kAccountKeyFilterNoNotificationHeader) + .AddExtraField(kAccountKeyFilter) + .AddExtraFieldHeader(kSaltHeader) + .AddExtraField(kSalt) + .Build() + ->CreateServiceData(); + } + + std::vector GetAdvWrongServicedata() { + return FastPairServiceDataCreator::Builder() + .SetHeader(kNotDiscoverableAdvHeader) + .SetModelId(kModelId) + .AddExtraFieldHeader /*InvalidType*/ (0b01100001) + .AddExtraField(kAccountKeyFilter) + .AddExtraFieldHeader(kSaltHeader) + .AddExtraField(kSalt) + .Build() + ->CreateServiceData(); + } + + SingleThreadExecutor executor_; + FastPairDeviceRepository devices_{&executor_}; + std::unique_ptr scanner_; + std::unique_ptr non_discoverable_scanner_; +}; + +TEST_F(FastPairNonDiscoverableScannerTest, FoundAssociatedDevice) { + const std::vector account_key_vec{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, + 0xCC, 0xDD, 0xEE, 0xFF}; + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + AccountKey account_key(account_key_vec); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(account_key, + kModelId); + + auto ble_peripheral = + std::make_unique(kBleAddress, GetAdvServicedata()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, + [&](FastPairDevice& device) { + EXPECT_EQ(device.GetModelId(), kModelId); + EXPECT_EQ(device.GetBleAddress(), kBleAddress); + EXPECT_EQ(device.GetProtocol(), Protocol::kFastPairSubsequentPairing); + EXPECT_EQ(device.GetAccountKey(), account_key); + EXPECT_TRUE(device.ShouldShowUiNotification()); + found_notification.Notify(); + }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + found_notification.WaitForNotification(); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + lost_notification.WaitForNotification(); +} + +TEST_F(FastPairNonDiscoverableScannerTest, FoundNonAssociatedDevice) { + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(std::nullopt, + std::nullopt); + + auto ble_peripheral = + std::make_unique(kBleAddress, GetAdvServicedata()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, [&](FastPairDevice& device) { found_notification.Notify(); }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + found_notification.WaitForNotificationWithTimeout(kFailureTimeout)); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + lost_notification.WaitForNotificationWithTimeout(kFailureTimeout)); +} + +TEST_F(FastPairNonDiscoverableScannerTest, FoundNoUIDevice) { + const std::vector account_key_vec{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, + 0xCC, 0xDD, 0xEE, 0xFF}; + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + AccountKey account_key(account_key_vec); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(account_key, + kModelId); + + auto ble_peripheral = + std::make_unique(kBleAddress, GetAdvNoUiServicedata()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, + [&](FastPairDevice& device) { + EXPECT_EQ(device.GetModelId(), kModelId); + EXPECT_EQ(device.GetBleAddress(), kBleAddress); + EXPECT_EQ(device.GetProtocol(), Protocol::kFastPairSubsequentPairing); + EXPECT_EQ(device.GetAccountKey(), account_key); + EXPECT_FALSE(device.ShouldShowUiNotification().value()); + found_notification.Notify(); + }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + found_notification.WaitForNotification(); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + lost_notification.WaitForNotification(); +} + +TEST_F(FastPairNonDiscoverableScannerTest, FailedToGetDeviceMetadata) { + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(AccountKey(), + kModelId); + + auto ble_peripheral = + std::make_unique(kBleAddress, GetAdvServicedata()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, [&](FastPairDevice& device) { found_notification.Notify(); }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + found_notification.WaitForNotificationWithTimeout(kFailureTimeout)); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + lost_notification.WaitForNotificationWithTimeout(kFailureTimeout)); +} + +TEST_F(FastPairNonDiscoverableScannerTest, NoServiceData) { + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(AccountKey(), + kModelId); + + auto ble_peripheral = + std::make_unique(kBleAddress, std::vector()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, [&](FastPairDevice& device) { found_notification.Notify(); }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + found_notification.WaitForNotificationWithTimeout(kFailureTimeout)); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + lost_notification.WaitForNotificationWithTimeout(kFailureTimeout)); +} + +TEST_F(FastPairNonDiscoverableScannerTest, FailedToParseNonDiscoverableAdv) { + scanner_ = std::make_unique(); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(AccountKey(), + kModelId); + + auto ble_peripheral = std::make_unique( + kBleAddress, GetAdvWrongServicedata()); + + absl::Notification found_notification; + absl::Notification lost_notification; + non_discoverable_scanner_ = FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, [&](FastPairDevice& device) { found_notification.Notify(); }, + [&](FastPairDevice& device) { lost_notification.Notify(); }, &executor_, + &devices_); + + scanner_->NotifyDeviceFound(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + found_notification.WaitForNotificationWithTimeout(kFailureTimeout)); + scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); + EXPECT_FALSE( + lost_notification.WaitForNotificationWithTimeout(kFailureTimeout)); +} + +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/scanning/scanner_broker_impl.cc b/fastpair/scanning/scanner_broker_impl.cc index 9d95ceae..1a38c53c 100644 --- a/fastpair/scanning/scanner_broker_impl.cc +++ b/fastpair/scanning/scanner_broker_impl.cc @@ -20,6 +20,7 @@ #include "absl/functional/bind_front.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h" +#include "fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h" #include "fastpair/scanning/fastpair/fast_pair_scanner_impl.h" #include "internal/platform/logging.h" @@ -59,6 +60,7 @@ void ScannerBrokerImpl::RemoveObserver(Observer* observer) { std::unique_ptr ScannerBrokerImpl::StartScanning(Protocol protocol) { DCHECK(!fast_pair_discoverable_scanner_); + DCHECK(!fast_pair_non_discoverable_scanner_); NEARBY_LOGS(VERBOSE) << "Starting Fast Pair Scanning."; scanner_ = std::make_unique(mediums_, executor_); fast_pair_discoverable_scanner_ = @@ -67,6 +69,13 @@ ScannerBrokerImpl::StartScanning(Protocol protocol) { absl::bind_front(&ScannerBrokerImpl::NotifyDeviceFound, this), absl::bind_front(&ScannerBrokerImpl::NotifyDeviceLost, this), executor_, device_repository_); + + fast_pair_non_discoverable_scanner_ = + FastPairNonDiscoverableScanner::Factory::Create( + *scanner_, + absl::bind_front(&ScannerBrokerImpl::NotifyDeviceFound, this), + absl::bind_front(&ScannerBrokerImpl::NotifyDeviceLost, this), + executor_, device_repository_); scanning_session_ = scanner_->StartScanning(); return std::make_unique(this, protocol); } @@ -77,6 +86,7 @@ void ScannerBrokerImpl::StopScanning(Protocol protocol) { observers_.Clear(); DestroyOnExecutor(std::move(fast_pair_discoverable_scanner_), executor_); + DestroyOnExecutor(std::move(fast_pair_non_discoverable_scanner_), executor_); DestroyOnExecutor(std::move(scanner_), executor_); } diff --git a/fastpair/scanning/scanner_broker_impl.h b/fastpair/scanning/scanner_broker_impl.h index 0152a3f4..5ec9950f 100644 --- a/fastpair/scanning/scanner_broker_impl.h +++ b/fastpair/scanning/scanner_broker_impl.h @@ -21,6 +21,7 @@ #include "fastpair/internal/mediums/mediums.h" #include "fastpair/repository/fast_pair_device_repository.h" #include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h" +#include "fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "fastpair/scanning/scanner_broker.h" #include "internal/base/observer_list.h" @@ -49,6 +50,8 @@ class ScannerBrokerImpl : public ScannerBroker { SingleThreadExecutor* executor_; std::unique_ptr scanner_; std::unique_ptr fast_pair_discoverable_scanner_; + std::unique_ptr + fast_pair_non_discoverable_scanner_; ObserverList observers_; FastPairDeviceRepository* device_repository_; std::unique_ptr scanning_session_; diff --git a/fastpair/scanning/scanner_broker_impl_test.cc b/fastpair/scanning/scanner_broker_impl_test.cc index 379c4816..58f3eb1c 100644 --- a/fastpair/scanning/scanner_broker_impl_test.cc +++ b/fastpair/scanning/scanner_broker_impl_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include "gtest/gtest.h" #include "absl/strings/escaping.h" @@ -24,8 +25,9 @@ #include "fastpair/common/protocol.h" #include "fastpair/internal/mediums/mediums.h" #include "fastpair/proto/fastpair_rpcs.proto.h" -#include "fastpair/scanning/scanner_broker.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "fastpair/scanning/scanner_broker.h" +#include "fastpair/testing/fast_pair_service_data_creator.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" @@ -33,8 +35,12 @@ namespace nearby { namespace fastpair { namespace { - constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(1000); +constexpr int kNotDiscoverableAdvHeader = 0b00000110; +constexpr int kAccountKeyFilterHeader = 0b01100000; +constexpr int kSaltHeader = 0b00010001; +constexpr absl::string_view kAccountKeyFilter("112233445566"); +constexpr absl::string_view kSalt("01"); constexpr absl::string_view kServiceID{"Fast Pair"}; constexpr absl::string_view kModelId{"718c17"}; constexpr absl::string_view kFastPairServiceUuid{ @@ -69,7 +75,7 @@ class ScannerBrokerImplTest : public testing::Test { MediumEnvironment& env_{MediumEnvironment::Instance()}; }; -TEST_F(ScannerBrokerImplTest, CanStartScanning) { +TEST_F(ScannerBrokerImplTest, FoundDiscoverableAdvertisement) { env_.Start(); // Setup FakeFastPairRepository std::string decoded_key; @@ -113,6 +119,60 @@ TEST_F(ScannerBrokerImplTest, CanStartScanning) { scanning_session.reset(); env_.Stop(); } + +TEST_F(ScannerBrokerImplTest, FoundNonDiscoverableAdvertisement) { + env_.Start(); + SingleThreadExecutor executor; + FastPairDeviceRepository devices(&executor); + auto repository = std::make_unique(); + proto::Device metadata; + repository->SetFakeMetadata(kModelId, metadata); + repository->SetResultOfCheckIfAssociatedWithCurrentAccount(AccountKey(), + kModelId); + + // Create Fast Pair Scanner and add its observer + Mediums mediums_1; + auto scanner_broker = + std::make_unique(mediums_1, &executor, &devices); + CountDownLatch accept_latch(1); + CountDownLatch lost_latch(1); + ScannerBrokerObserver observer(scanner_broker.get(), &accept_latch, + &lost_latch); + + // Create Advertiser and startAdvertising + Mediums mediums_2; + std::string service_id(kServiceID); + std::vector service_data = + FastPairServiceDataCreator::Builder() + .SetHeader(kNotDiscoverableAdvHeader) + .SetModelId(kModelId) + .AddExtraFieldHeader(kAccountKeyFilterHeader) + .AddExtraField(kAccountKeyFilter) + .AddExtraFieldHeader(kSaltHeader) + .AddExtraField(kSalt) + .Build() + ->CreateServiceData(); + ByteArray advertisement_bytes( + std::string(service_data.begin(), service_data.end())); + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_2.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + // Fast Pair scanner startScanning + auto scanning_session = + scanner_broker->StartScanning(Protocol::kFastPairInitialPairing); + + // Notify device found + EXPECT_TRUE(accept_latch.Await(kTaskWaitTimeout).result()); + + // Advertiser stopAdvertising + mediums_2.GetBle().GetMedium().StopAdvertising(service_id); + + // Notify device lost + EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); + scanning_session.reset(); + env_.Stop(); +} } // namespace } // namespace fastpair } // namespace nearby