diff --git a/fastpair/repository/fake_fast_pair_repository.h b/fastpair/repository/fake_fast_pair_repository.h index ba387743..93016fbd 100644 --- a/fastpair/repository/fake_fast_pair_repository.h +++ b/fastpair/repository/fake_fast_pair_repository.h @@ -56,6 +56,10 @@ class FakeFastPairRepository : public FastPairRepository { const AccountKey& account_key, OperationToFootprintsCallback callback) override{}; + void CheckIfAssociatedWithCurrentAccount( + AccountKeyFilter& account_key_filter, + CheckAccountKeysCallback callback) override{}; + private: absl::flat_hash_map> data_; SingleThreadExecutor executor_; diff --git a/fastpair/repository/fast_pair_repository.h b/fastpair/repository/fast_pair_repository.h index 10eab502..7134c0fe 100644 --- a/fastpair/repository/fast_pair_repository.h +++ b/fastpair/repository/fast_pair_repository.h @@ -23,6 +23,7 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "fastpair/common/account_key.h" +#include "fastpair/common/account_key_filter.h" #include "fastpair/common/device_metadata.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/proto/data.proto.h" @@ -32,6 +33,9 @@ namespace nearby { namespace fastpair { using DeviceMetadataCallback = absl::AnyInvocable device_metadata)>; +using CheckAccountKeysCallback = + absl::AnyInvocable account_key, + std::optional model_id)>; using OperationToFootprintsCallback = absl::AnyInvocable; @@ -75,6 +79,12 @@ class FastPairRepository { const AccountKey& account_key, OperationToFootprintsCallback callback) = 0; + // Checks all account keys associated with current user's account against the + // given filter. If a match is found, return the account key. + virtual void CheckIfAssociatedWithCurrentAccount( + AccountKeyFilter& account_key_filter, + CheckAccountKeysCallback callback) = 0; + protected: static void SetInstance(FastPairRepository* instance); }; diff --git a/fastpair/repository/fast_pair_repository_impl.cc b/fastpair/repository/fast_pair_repository_impl.cc index 1ae227ac..2f51044b 100644 --- a/fastpair/repository/fast_pair_repository_impl.cc +++ b/fastpair/repository/fast_pair_repository_impl.cc @@ -188,11 +188,47 @@ void FastPairRepositoryImpl::GetUserSavedDevices() { } NEARBY_LOGS(INFO) << __func__ << ": Got " << saved_devices.size() << " saved devices."; + // TODO(b/289139378) : save device's in local cache. for (auto& observer : observers_.GetObservers()) { observer->OnGetUserSavedDevices(opt_in_status, saved_devices); } }); } +void FastPairRepositoryImpl::CheckIfAssociatedWithCurrentAccount( + AccountKeyFilter& account_key_filter, CheckAccountKeysCallback callback) { + executor_.Execute("Check if associated.", [this, + account_key_filter = + std::move(account_key_filter), + callback = std::move( + callback)]() mutable { + NEARBY_LOGS(INFO) << __func__ + << ": Start to check if associated with current account."; + proto::UserReadDevicesRequest request; + absl::StatusOr response = + fast_pair_client_->UserReadDevices(request); + if (response.ok()) { + for (const auto& info : response->fast_pair_info()) { + if (!info.has_device()) { + continue; + } + AccountKey account_key(info.device().account_key()); + if (!account_key_filter.IsPossiblyInSet(account_key)) { + continue; + } + proto::StoredDiscoveryItem device; + if (device.ParseFromString(info.device().discovery_item_bytes())) { + NEARBY_LOGS(INFO) + << "Account key matched with a paired device: " << device.title(); + std::move(callback)(account_key, device.id()); + return; + } + } + } + NEARBY_LOGS(INFO) << "Account key does not match any paired devices."; + std::move(callback)(std::nullopt, std::nullopt); + }); +} + } // namespace fastpair } // namespace nearby diff --git a/fastpair/repository/fast_pair_repository_impl.h b/fastpair/repository/fast_pair_repository_impl.h index 5f880785..74fb4abc 100644 --- a/fastpair/repository/fast_pair_repository_impl.h +++ b/fastpair/repository/fast_pair_repository_impl.h @@ -51,6 +51,10 @@ class FastPairRepositoryImpl : public FastPairRepository { const AccountKey& account_key, OperationToFootprintsCallback callback) override; + void CheckIfAssociatedWithCurrentAccount( + AccountKeyFilter& account_key_filter, + CheckAccountKeysCallback callback) override; + private: // A thread for running blocking tasks. SingleThreadExecutor executor_; diff --git a/fastpair/repository/fast_pair_repository_impl_test.cc b/fastpair/repository/fast_pair_repository_impl_test.cc index c27edffd..a40f4162 100644 --- a/fastpair/repository/fast_pair_repository_impl_test.cc +++ b/fastpair/repository/fast_pair_repository_impl_test.cc @@ -373,6 +373,101 @@ TEST(FastPairRepositoryImplTest, FailedToDeleteAssociatedDeviceWithError) { }); latch.Await(); } + +// Test data comes from: +// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases +TEST(FastPairRepositoryImplTest, DeviceAssociatedWithCurrentAccountSuccess) { + const std::vector filter{0x02, 0x0C, 0x80, 0x2A}; + const std::vector salt{0xC7, 0xC8}; + const std::vector account_key_vec{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, + 0xCC, 0xDD, 0xEE, 0xFF}; + FakeFastPairClient fake_fast_pair_client; + auto fast_pair_repository = + std::make_unique(&fake_fast_pair_client); + + // Sets up two devices to proto::UserReadDevicesResponse. + proto::UserReadDevicesResponse response_proto; + // Adds device 1. + auto* fast_pair_info_1 = response_proto.add_fast_pair_info(); + fast_pair_info_1->set_opt_in_status( + proto::OptInStatus::OPT_IN_STATUS_OPTED_IN); + + // Adds device 2. + FastPairDevice device_2(kHexModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + AccountKey account_key(account_key_vec); + device_2.SetAccountKey(account_key); + device_2.SetPublicAddress(kPublicAddress); + device_2.SetDisplayName(kDisplayName); + proto::GetObservedDeviceResponse get_observed_device_response_1; + auto* observed_device_strings_1 = + get_observed_device_response_1.mutable_strings(); + observed_device_strings_1->set_initial_pairing_description( + kInitialPairingdescription); + DeviceMetadata device_metadata_1(get_observed_device_response_1); + device_2.SetMetadata(device_metadata_1); + auto* fast_pair_info_2 = response_proto.add_fast_pair_info(); + BuildFastPairInfo(fast_pair_info_2, device_2); + + fake_fast_pair_client.SetUserReadDevicesResponse(response_proto); + + AccountKeyFilter account_key_filter(filter, salt); + + CountDownLatch latch(1); + // Get user's saved device from footprints. + fast_pair_repository->CheckIfAssociatedWithCurrentAccount( + account_key_filter, [&](std::optional cb_account_key, + std::optional cb_model_id) { + ASSERT_TRUE(cb_account_key.has_value()); + ASSERT_TRUE(cb_model_id.has_value()); + EXPECT_EQ(cb_account_key.value(), account_key); + EXPECT_EQ(cb_model_id.value(), kHexModelId); + latch.CountDown(); + }); + latch.Await(); +} + +TEST(FastPairRepositoryImplTest, DeviceNotAssociatedWithCurrentAccount) { + const std::vector filter{0x02, 0x0C, 0x80, 0x2A}; + const std::vector salt{0xC7, 0xC8}; + const std::vector account_key_vec{0x11, 0x11, 0x22, 0x22, 0x33, 0x33, + 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, + 0x77, 0x77, 0x88, 0x88}; + + FakeFastPairClient fake_fast_pair_client; + auto fast_pair_repository = + std::make_unique(&fake_fast_pair_client); + + // Sets up two devices to proto::UserReadDevicesResponse. + // Adds device 1. + proto::UserReadDevicesResponse response_proto; + FastPairDevice device(kHexModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + AccountKey account_key(account_key_vec); + device.SetAccountKey(account_key); + device.SetPublicAddress(kPublicAddress); + device.SetDisplayName(kDisplayName); + proto::GetObservedDeviceResponse get_observed_device_response; + DeviceMetadata device_metadata(get_observed_device_response); + device.SetMetadata(device_metadata); + auto* fast_pair_info = response_proto.add_fast_pair_info(); + BuildFastPairInfo(fast_pair_info, device); + fake_fast_pair_client.SetUserReadDevicesResponse(response_proto); + + AccountKeyFilter account_key_filter(filter, salt); + + CountDownLatch latch(1); + // Get user's saved device from footprints. + fast_pair_repository->CheckIfAssociatedWithCurrentAccount( + account_key_filter, [&](std::optional cb_account_key, + std::optional cb_model_id) { + EXPECT_FALSE(cb_account_key.has_value()); + EXPECT_FALSE(cb_model_id.has_value()); + latch.CountDown(); + }); + latch.Await(); +} } // namespace } // namespace fastpair } // namespace nearby