diff --git a/fastpair/internal/BUILD b/fastpair/internal/BUILD index cbec33b4..3742f983 100644 --- a/fastpair/internal/BUILD +++ b/fastpair/internal/BUILD @@ -14,6 +14,7 @@ cc_library( "//fastpair:fast_pair_controller", "//fastpair:fast_pair_events", "//fastpair:fast_pair_seeker", + "//fastpair/common", "//fastpair/internal/mediums", "//fastpair/pairing", "//fastpair/repository", @@ -39,6 +40,9 @@ cc_test( "//fastpair/common", "//fastpair/message_stream:fake_gatt_callbacks", "//fastpair/message_stream:fake_provider", + "//fastpair/proto:fastpair_cc_proto", + "//fastpair/repository", + "//fastpair/repository:device_repository", "//fastpair/repository:test_support", "//internal/account:test_support", "//internal/platform:test_util", @@ -46,7 +50,8 @@ cc_test( "//internal/platform/implementation/g3", # build_cleaner: keep "//internal/test/google3_only:test", "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/time", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index 10f7bafe..30b27f95 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -21,11 +21,13 @@ #include "absl/status/status.h" #include "absl/strings/str_format.h" +#include "fastpair/common/account_key.h" #include "fastpair/fast_pair_controller.h" #include "fastpair/fast_pair_events.h" #include "fastpair/pairing/pairer_broker_impl.h" #include "fastpair/scanning/scanner_broker_impl.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" #include "internal/platform/pending_job_registry.h" #include "internal/platform/single_thread_executor.h" @@ -303,5 +305,23 @@ void FastPairSeekerImpl::OnRetroactivePairFound(FastPairDevice& device) { callbacks_.on_pair_event(device, PairEvent{.is_paired = true}); } +void FastPairSeekerImpl::ForgetDeviceByAccountKey( + const AccountKey& account_key) { + NEARBY_LOGS(VERBOSE) << __func__; + auto opt_device = devices_->FindDevice(account_key); + if (!opt_device.has_value()) { + NEARBY_LOGS(INFO) << __func__ << "No FP device matching the account key."; + } else { + devices_->RemoveDevice(opt_device.value()); + } + + repository_->DeleteAssociatedDeviceByAccountKey( + account_key, [&](absl::Status success) { + if (!success.ok()) return; + NEARBY_LOGS(VERBOSE) << "Deleted associated devcie by account key"; + // Temporary solution to refresh the saved_devices_sheet. + repository_->GetUserSavedDevices(); + }); +} } // namespace fastpair } // namespace nearby diff --git a/fastpair/internal/fast_pair_seeker_impl.h b/fastpair/internal/fast_pair_seeker_impl.h index 8984b22e..bf096f2b 100644 --- a/fastpair/internal/fast_pair_seeker_impl.h +++ b/fastpair/internal/fast_pair_seeker_impl.h @@ -43,6 +43,7 @@ class FastPairSeekerExt : public FastPairSeeker { // Handle the state changes of screen lock. virtual void SetIsScreenLocked(bool is_locked) = 0; + virtual void ForgetDeviceByAccountKey(const AccountKey& account_key) = 0; }; class FastPairSeekerImpl : public FastPairSeekerExt, @@ -91,6 +92,7 @@ class FastPairSeekerImpl : public FastPairSeekerExt, absl::Status StartFastPairScan() override; absl::Status StopFastPairScan() override; void SetIsScreenLocked(bool is_locked) override; + void ForgetDeviceByAccountKey(const AccountKey& account_key) override; // From BluetoothClassicMedium::Observer. void DeviceAdded(BluetoothDevice& device) override; diff --git a/fastpair/internal/fast_pair_seeker_impl_test.cc b/fastpair/internal/fast_pair_seeker_impl_test.cc index 9957265c..09556b8e 100644 --- a/fastpair/internal/fast_pair_seeker_impl_test.cc +++ b/fastpair/internal/fast_pair_seeker_impl_test.cc @@ -18,18 +18,27 @@ #include #include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/escaping.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_events.h" #include "fastpair/fast_pair_seeker.h" #include "fastpair/message_stream/fake_gatt_callbacks.h" #include "fastpair/message_stream/fake_provider.h" +#include "fastpair/proto/data.proto.h" +#include "fastpair/proto/enum.proto.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/repository/fast_pair_repository.h" #include "internal/account/fake_account_manager.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" #include "internal/platform/task_runner_impl.h" @@ -61,6 +70,19 @@ class MediumEnvironmentStarter { ~MediumEnvironmentStarter() { MediumEnvironment::Instance().Stop(); } }; +class FastPairRepositoryObserver : public FastPairRepository::Observer { + public: + explicit FastPairRepositoryObserver(CountDownLatch* latch) { latch_ = latch; } + + void OnGetUserSavedDevices( + const proto::OptInStatus& opt_in_status, + const std::vector& devices) override { + latch_->CountDown(); + } + + CountDownLatch* latch_ = nullptr; +}; + class FastPairSeekerImplTest : public testing::Test { protected: FastPairSeekerImplTest() { @@ -222,6 +244,63 @@ TEST_F(FastPairSeekerImplTest, InitialPairing) { fast_pair_seeker_.reset(); } +TEST_F(FastPairSeekerImplTest, ForgetDeviceByAccountKey) { + NEARBY_LOG_SET_SEVERITY(VERBOSE); + FakeProvider provider; + CountDownLatch discover_latch(1); + CountDownLatch pair_latch(1); + fast_pair_seeker_ = std::make_unique( + FastPairSeekerImpl::ServiceCallbacks{ + .on_initial_discovery = + [&](const FastPairDevice& device, InitialDiscoveryEvent event) { + EXPECT_EQ(device.GetModelId(), kModelId); + EXPECT_OK(fast_pair_seeker_->StartInitialPairing( + device, {}, + {.on_pairing_result = [&](const FastPairDevice& device, + absl::Status status) { + EXPECT_EQ(device.GetBleAddress(), + provider.GetMacAddress()); + EXPECT_OK(status); + pair_latch.CountDown(); + }})); + discover_latch.CountDown(); + }}, + &executor_, account_manager_.get(), &devices_, repository_.get()); + + EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); + provider.PrepareForInitialPairing( + { + .private_key = absl::HexStringToBytes(kBobPrivateKey), + .public_key = absl::HexStringToBytes(kBobPublicKey), + .model_id = std::string(kModelId), + .pass_key = std::string(kPasskey), + }, + &fake_gatt_callbacks_); + + discover_latch.Await(); + pair_latch.Await(); + auto fp_device = devices_.FindDevice(provider.GetMacAddress()); + ASSERT_TRUE(fp_device.has_value()); + EXPECT_EQ(provider.GetAccountKey(), fp_device.value()->GetAccountKey()); + + // Adds FastPairRepository observer. + CountDownLatch repository_latch(1); + FastPairRepositoryObserver observer(&repository_latch); + repository_->AddObserver(&observer); + // Adds FastPairDeviceRepository observer. + CountDownLatch devices_latch(1); + FastPairDeviceRepository::RemoveDeviceCallback callback = + [&](const FastPairDevice& device) { devices_latch.CountDown(); }; + devices_.AddObserver(&callback); + + fast_pair_seeker_->ForgetDeviceByAccountKey( + fp_device.value()->GetAccountKey()); + repository_latch.Await(); + devices_latch.Await(); + EXPECT_FALSE(devices_.FindDevice(provider.GetMacAddress()).has_value()); + fast_pair_seeker_.reset(); +} + TEST_F(FastPairSeekerImplTest, RetroactivePairingWithUserConsent) { NEARBY_LOG_SET_SEVERITY(VERBOSE); FakeProvider provider; diff --git a/fastpair/repository/BUILD b/fastpair/repository/BUILD index c6b3dff2..747a6462 100644 --- a/fastpair/repository/BUILD +++ b/fastpair/repository/BUILD @@ -81,6 +81,7 @@ cc_library( ":repository", "//fastpair/common", "//fastpair/proto:fastpair_cc_proto", + "//internal/base", "//internal/platform:types", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", @@ -115,6 +116,7 @@ cc_test( "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings:string_view", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/repository/fake_fast_pair_repository.cc b/fastpair/repository/fake_fast_pair_repository.cc index 4c0491d7..713a615d 100644 --- a/fastpair/repository/fake_fast_pair_repository.cc +++ b/fastpair/repository/fake_fast_pair_repository.cc @@ -18,16 +18,31 @@ #include #include #include +#include +#include "absl/status/status.h" #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "fastpair/common/account_key.h" +#include "fastpair/common/account_key_filter.h" #include "fastpair/common/constant.h" +#include "fastpair/common/device_metadata.h" #include "fastpair/common/fast_pair_device.h" +#include "fastpair/proto/data.proto.h" +#include "fastpair/proto/enum.proto.h" #include "fastpair/proto/fastpair_rpcs.proto.h" +#include "fastpair/repository/fast_pair_repository.h" namespace nearby { namespace fastpair { +void FakeFastPairRepository::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeFastPairRepository::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + void FakeFastPairRepository::SetFakeMetadata(absl::string_view hex_model_id, proto::Device metadata) { proto::GetObservedDeviceResponse response; @@ -73,6 +88,14 @@ void FakeFastPairRepository::GetDeviceMetadata( }); } +void FakeFastPairRepository::GetUserSavedDevices() { + proto::OptInStatus opt_in_status = proto::OptInStatus::OPT_IN_STATUS_UNKNOWN; + std::vector saved_devices; + for (auto& observer : observers_.GetObservers()) { + observer->OnGetUserSavedDevices(opt_in_status, saved_devices); + } +} + void FakeFastPairRepository::WriteAccountAssociationToFootprints( FastPairDevice& device, OperationCallback callback) { executor_.Execute([callback = std::move(callback), this]() mutable { diff --git a/fastpair/repository/fake_fast_pair_repository.h b/fastpair/repository/fake_fast_pair_repository.h index 5afb3d24..ef1a7619 100644 --- a/fastpair/repository/fake_fast_pair_repository.h +++ b/fastpair/repository/fake_fast_pair_repository.h @@ -25,6 +25,7 @@ #include "fastpair/common/account_key.h" #include "fastpair/common/device_metadata.h" #include "fastpair/repository/fast_pair_repository.h" +#include "internal/base/observer_list.h" #include "internal/platform/single_thread_executor.h" namespace nearby { @@ -49,13 +50,13 @@ class FakeFastPairRepository : public FastPairRepository { void SetResultOfIsDeviceSavedToAccount(absl::Status status); // FastPairRepository:: - void AddObserver(Observer* observer) override{}; - void RemoveObserver(Observer* observer) override{}; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; void GetDeviceMetadata(absl::string_view hex_model_id, DeviceMetadataCallback callback) override; - void GetUserSavedDevices() override{}; + void GetUserSavedDevices() override; void WriteAccountAssociationToFootprints(FastPairDevice& device, OperationCallback callback) override; @@ -82,6 +83,7 @@ class FakeFastPairRepository : public FastPairRepository { absl::Status deleted_associated_device_; // Results of IsDeviceSavedToAccount absl::Status is_device_saved_to_account_; + ObserverList observers_; SingleThreadExecutor executor_; }; } // namespace fastpair diff --git a/fastpair/repository/fast_pair_device_repository.cc b/fastpair/repository/fast_pair_device_repository.cc index d210d94d..e219a484 100644 --- a/fastpair/repository/fast_pair_device_repository.cc +++ b/fastpair/repository/fast_pair_device_repository.cc @@ -19,6 +19,8 @@ #include #include +#include "fastpair/common/account_key.h" +#include "fastpair/common/fast_pair_device.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" @@ -51,7 +53,7 @@ void FastPairDeviceRepository::RemoveDevice(const FastPairDevice* device) { for (auto* callback : observers_.GetObservers()) { (*callback)(*fast_pair_device); } - NEARBY_LOGS(VERBOSE) << "Destroyed FP device: " << fast_pair_device; + NEARBY_LOGS(VERBOSE) << "Destroyed FP device: " << *fast_pair_device; }); } @@ -70,6 +72,20 @@ std::optional FastPairDeviceRepository::FindDevice( } } +std::optional FastPairDeviceRepository::FindDevice( + const AccountKey& account_key) { + MutexLock lock(&mutex_); + auto it = std::find_if(devices_.begin(), devices_.end(), + [&](const std::unique_ptr& device) { + return device->GetAccountKey() == account_key; + }); + if (it != devices_.end()) { + return it->get(); + } else { + return std::nullopt; + } +} + std::unique_ptr FastPairDeviceRepository::ExtractDevice( const FastPairDevice* device) { MutexLock lock(&mutex_); diff --git a/fastpair/repository/fast_pair_device_repository.h b/fastpair/repository/fast_pair_device_repository.h index ad70bb32..5240122f 100644 --- a/fastpair/repository/fast_pair_device_repository.h +++ b/fastpair/repository/fast_pair_device_repository.h @@ -54,6 +54,9 @@ class FastPairDeviceRepository { // or BLE. std::optional FindDevice(absl::string_view mac_address); + // Finds a device matching the account key. + std::optional FindDevice(const AccountKey& account_key); + void AddObserver(RemoveDeviceCallback* observer) { observers_.AddObserver(observer); } diff --git a/fastpair/repository/fast_pair_device_repository_test.cc b/fastpair/repository/fast_pair_device_repository_test.cc index 3418ae4c..e476e1d0 100644 --- a/fastpair/repository/fast_pair_device_repository_test.cc +++ b/fastpair/repository/fast_pair_device_repository_test.cc @@ -17,9 +17,9 @@ #include #include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "fastpair/common/account_key.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/protocol.h" #include "internal/platform/single_thread_executor.h" @@ -31,6 +31,7 @@ namespace { constexpr absl::string_view kModelId = "123456"; constexpr absl::string_view kBleAddress = "AA:BB:CC:DD:EE:FF"; constexpr absl::string_view kBtAddress = "12:34:56:78:90:AB"; +constexpr absl::string_view kAccountKey = "04b85786180add47fb81a04a8ce6b0de"; TEST(FastPairDeviceRepositoryTest, AddDevice) { SingleThreadExecutor executor; @@ -76,6 +77,24 @@ TEST(FastPairDeviceRepositoryTest, FindDeviceByBtAddress) { executor.Shutdown(); } +TEST(FastPairDeviceRepositoryTest, FindDeviceByAccountKey) { + SingleThreadExecutor executor; + FastPairDeviceRepository repo(&executor); + auto fast_pair_device = + std::make_unique(Protocol::kFastPairInitialPairing); + fast_pair_device->SetPublicAddress(kBtAddress); + fast_pair_device->SetAccountKey(AccountKey(kAccountKey)); + repo.AddDevice(std::move(fast_pair_device)); + + auto opt_device = repo.FindDevice(AccountKey(kAccountKey)); + + ASSERT_TRUE(opt_device.has_value()); + FastPairDevice* device = opt_device.value(); + ASSERT_NE(device, nullptr); + EXPECT_EQ(device->GetAccountKey().GetAsBytes(), kAccountKey); + executor.Shutdown(); +} + TEST(FastPairDeviceRepositoryTest, RemoveDevice) { SingleThreadExecutor executor; FastPairDeviceRepository repo(&executor);