From aefef911144bb572f70595027f3458c7dcc199fe Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 5 Jun 2023 15:41:41 -0700 Subject: [PATCH] Add UI Discovery check and show into Mediator. PiperOrigin-RevId: 538000396 --- fastpair/keyed_service/BUILD | 15 +- fastpair/keyed_service/fast_pair_mediator.cc | 95 ++++++- fastpair/keyed_service/fast_pair_mediator.h | 47 +++- .../keyed_service/fast_pair_mediator_test.cc | 248 ++++++++++++++++-- 4 files changed, 369 insertions(+), 36 deletions(-) diff --git a/fastpair/keyed_service/BUILD b/fastpair/keyed_service/BUILD index 0eaa6bb7..4992e71a 100644 --- a/fastpair/keyed_service/BUILD +++ b/fastpair/keyed_service/BUILD @@ -28,11 +28,13 @@ cc_library( ], deps = [ "//fastpair/common", + "//fastpair/internal/mediums", + "//fastpair/repository:device_repository", "//fastpair/scanning:scanner", "//fastpair/server_access", + "//fastpair/ui:fast_pair_ui", "//internal/platform:logging", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/strings", + "//internal/platform:types", ], ) @@ -45,10 +47,15 @@ cc_test( shard_count = 16, deps = [ ":keyed_service", - "//fastpair/scanning:mocks", - "//fastpair/server_access:mocks", + "//fastpair/server_access:test_support", + "//fastpair/testing", + "//fastpair/ui:fast_pair_ui", + "//fastpair/ui:mock_fast_pair_ui", + "//internal/platform:test_util", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index 2f84dee1..005b4acb 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -18,26 +18,92 @@ #include #include "fastpair/common/protocol.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/scanning/scanner_broker_impl.h" +#include "fastpair/server_access/fast_pair_repository_impl.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" +#include "fastpair/ui/ui_broker_impl.h" #include "internal/platform/logging.h" +#include "internal/platform/single_thread_executor.h" namespace nearby { namespace fastpair { -Mediator::Mediator(std::unique_ptr scanner_broker, - std::unique_ptr fast_pair_repository) - : scanner_broker_(std::move(scanner_broker)), - fast_pair_repository_(std::move(fast_pair_repository)) { + +Mediator::Mediator( + std::unique_ptr mediums, std::unique_ptr ui_broker, + std::unique_ptr notification_controller, + std::unique_ptr fast_pair_repository, + std::unique_ptr executor) + : mediums_(std::move(mediums)), + ui_broker_(std::move(ui_broker)), + notification_controller_(std::move(notification_controller)), + fast_pair_repository_(std::move(fast_pair_repository)), + executor_(std::move(executor)) { + devices_ = std::make_unique(executor_.get()); + scanner_broker_ = std::make_unique( + *mediums_, executor_.get(), devices_.get()); scanner_broker_->AddObserver(this); + ui_broker_->AddObserver(this); } void Mediator::OnDeviceFound(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; + + if (IsDeviceCurrentlyShowingNotification(device)) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": Extending notification for re-discovered device=" + << *device_currently_showing_notification_; + // TODO(b/278768167): Add ui_broker_->ExtendNotification(); + return; + } else if (device_currently_showing_notification_) { + NEARBY_LOGS(VERBOSE) + << __func__ + << ": Already showing a notification for a different device= " + << *device_currently_showing_notification_; + return; + } // Show discovery notification + device_currently_showing_notification_ = &device; + ui_broker_->ShowDiscovery(device, *notification_controller_); } void Mediator::OnDeviceLost(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; } +void Mediator::OnDiscoveryAction(const FastPairDevice& device, + DiscoveryAction action) { + switch (action) { + case DiscoveryAction::kPairToDevice: + NEARBY_LOGS(INFO) << __func__ << ": Action = kPairToDevice"; + // TODO(285451051): Adding show pairing for higher than v1 version in ui + // broker + // TODO(282022590): Adding pairer broker to pair device + break; + case DiscoveryAction::kDismissedByOs: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByOs"; + break; + case DiscoveryAction::kDismissedByUser: + // When the user explicitly dismisses the discovery notification, update + // the device's block-list value accordingly. + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByUser"; + // TODO(285453663): update discovery block list + [[fallthrough]]; + case DiscoveryAction::kDismissedByTimeout: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByTimeout"; + device_currently_showing_notification_ = nullptr; + break; + case DiscoveryAction::kLearnMore: + NEARBY_LOGS(INFO) << __func__ << ": Action = kLearnMore"; + break; + default: + NEARBY_LOGS(INFO) << __func__ << ": Action = kUnknow"; + break; + } +} + void Mediator::StartScanning() { if (IsFastPairEnabled()) { scanning_session_ = @@ -48,10 +114,29 @@ void Mediator::StartScanning() { } bool Mediator::IsFastPairEnabled() { - // TODO(b/275452353): Add feature_status_tracker IsFastPairEnabled() + // TODO(b/275452353): Add feature_status_tracker IsFastPairEnabled() // Currently default to true. NEARBY_LOGS(VERBOSE) << __func__ << ": " << true; return true; } + +bool Mediator::IsDeviceCurrentlyShowingNotification( + const FastPairDevice& device) { + // BLE addresses could have rotated, causing this check to return false for + // the same device. Fast Pair considers a device different if they have + // different BLE addresses. Similarly, the this check will fail if it is the + // same physical device under different scenarios: for example, if a device + // is found via the initial scenario and via the subsequent scenario, Fast + // Pair does not consider them the same device. + + return device_currently_showing_notification_ && + device_currently_showing_notification_->GetModelId() == + device.GetModelId() && + device_currently_showing_notification_->GetBleAddress() == + device.GetBleAddress() && + device_currently_showing_notification_->GetProtocol() != + device.GetProtocol(); +} + } // namespace fastpair } // namespace nearby diff --git a/fastpair/keyed_service/fast_pair_mediator.h b/fastpair/keyed_service/fast_pair_mediator.h index 1b25b303..d38f0068 100644 --- a/fastpair/keyed_service/fast_pair_mediator.h +++ b/fastpair/keyed_service/fast_pair_mediator.h @@ -17,20 +17,44 @@ #include +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/repository/fast_pair_device_repository.h" #include "fastpair/scanning/scanner_broker.h" #include "fastpair/server_access/fast_pair_repository.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" +#include "fastpair/ui/ui_broker.h" +#include "internal/platform/single_thread_executor.h" namespace nearby { namespace fastpair { // Implements the Mediator design pattern for the components in the Fast Pair -class Mediator final : public ScannerBroker::Observer { +class Mediator final : public ScannerBroker::Observer, + public UIBroker::Observer { public: - Mediator(std::unique_ptr scanner_broker, - std::unique_ptr fast_pair_repository); + Mediator( + std::unique_ptr mediums, std::unique_ptr ui_broker, + std::unique_ptr notification_controller, + std::unique_ptr fast_pair_repository, + std::unique_ptr executor); Mediator(const Mediator&) = delete; Mediator& operator=(const Mediator&) = delete; - ~Mediator() override = default; + ~Mediator() override { + if (scanning_session_ == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << "scanner is not running"; + } + scanning_session_.reset(); + scanner_broker_->RemoveObserver(this); + DestroyOnExecutor(std::move(scanner_broker_), executor_.get()); + scanner_broker_.reset(); + ui_broker_->RemoveObserver(this); + ui_broker_.reset(); + }; + + FastPairNotificationController* GetNotificationController() { + return notification_controller_.get(); + } // ScannerBroker::Observer void OnDeviceFound(FastPairDevice& device) override; @@ -38,12 +62,27 @@ class Mediator final : public ScannerBroker::Observer { void StartScanning(); + // UIBroker::Observer + void OnDiscoveryAction(const FastPairDevice& device, + DiscoveryAction action) override; + private: bool IsFastPairEnabled(); + bool IsDeviceCurrentlyShowingNotification(const FastPairDevice& device); + + // |device_currently_showing_notification_| can be null if there is no + // notification currently displayed to the user. + FastPairDevice* device_currently_showing_notification_ = nullptr; + + std::unique_ptr mediums_; std::unique_ptr scanner_broker_; std::unique_ptr scanning_session_; + std::unique_ptr ui_broker_; + std::unique_ptr notification_controller_; std::unique_ptr fast_pair_repository_; + std::unique_ptr executor_; + std::unique_ptr devices_; }; } // namespace fastpair diff --git a/fastpair/keyed_service/fast_pair_mediator_test.cc b/fastpair/keyed_service/fast_pair_mediator_test.cc index 40944a49..8e8cee0d 100644 --- a/fastpair/keyed_service/fast_pair_mediator_test.cc +++ b/fastpair/keyed_service/fast_pair_mediator_test.cc @@ -14,56 +14,258 @@ #include "fastpair/keyed_service/fast_pair_mediator.h" +#include #include +#include #include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" -#include "fastpair/scanning/mock_scanner_broker.h" -#include "fastpair/server_access/mock_fast_pair_repository.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "fastpair/server_access/fake_fast_pair_repository.h" +#include "fastpair/testing/fast_pair_service_data_creator.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" +#include "fastpair/ui/fast_pair/mock_fast_pair_notification_controller.h" +#include "fastpair/ui/mock_ui_broker.h" +#include "fastpair/ui/ui_broker.h" +#include "fastpair/ui/ui_broker_impl.h" +#include "internal/platform/medium_environment.h" +#include "internal/platform/single_thread_executor.h" namespace nearby { namespace fastpair { namespace { +constexpr absl::string_view kModelId = "718c17"; +constexpr absl::string_view kServiceID = "Fast Pair"; +constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(1000); +constexpr absl::string_view kFastPairServiceUuid = + "0000FE2C-0000-1000-8000-00805F9B34FB"; +constexpr absl::string_view kPublicAntiSpoof = + "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" + "0wVcljfT3XPoiy1fntlneziyLD5knDVAJSE+RM/zlPRP/Jg=="; +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 kModelId2 = "9adb11"; +constexpr absl::string_view kPublicAntiSpoof2 = + "z+grhW8lWVA34JUQhXOxMrk1WqVy+VpEDd2K+01ZJvS6KdV0OUg7FRMzq+" + "ITuOqKO/2TIRKEAEfMKdyk2Ob1Vw=="; +constexpr absl::string_view kAddress = "74:74:46:01:6C:21"; + class MediatorTest : public testing::Test { public: void SetUp() override { - scanner_broker_ = std::make_unique(); - mock_scanner_broker_ = - static_cast(scanner_broker_.get()); + env_.Start(); + mediums_ = std::make_unique(); - fast_pair_repository_ = std::make_unique(); - mock_fast_pair_repository_ = - static_cast(fast_pair_repository_.get()); + // Setup FakeFastPairRepository for two devices + repository_ = FakeFastPairRepository::Create(kModelId, kPublicAntiSpoof); + + std::string decoded_key; + absl::Base64Unescape(kPublicAntiSpoof2, &decoded_key); + proto::Device metadata; + metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key); + repository_->SetFakeMetadata(kModelId2, metadata); + + ui_broker_ = std::make_unique(); + mock_ui_broker_ = static_cast(ui_broker_.get()); + + notification_controller_ = + std::make_unique(); + + executor_ = std::make_unique(); } void TearDown() override { - scanner_broker_.reset(); - fast_pair_repository_.reset(); + executor_.reset(); + mediums_.reset(); + repository_.reset(); + ui_broker_.reset(); + mock_ui_broker_ = nullptr; + notification_controller_.reset(); mediator_.reset(); - mock_scanner_broker_ = nullptr; - mock_fast_pair_repository_ = nullptr; + env_.Stop(); } protected: - std::unique_ptr scanner_broker_; - std::unique_ptr fast_pair_repository_; - MockScannerBroker* mock_scanner_broker_; - MockFastPairRepository* mock_fast_pair_repository_; + MediumEnvironment& env_{MediumEnvironment::Instance()}; + std::unique_ptr mediums_; + std::unique_ptr repository_; + std::unique_ptr ui_broker_; + std::unique_ptr notification_controller_; + std::unique_ptr executor_; + MockUIBroker* mock_ui_broker_; std::unique_ptr mediator_; }; -TEST_F(MediatorTest, SannerBrokerCallStartScanning) { - EXPECT_CALL(*mock_scanner_broker_, StartScanning); - mediator_ = std::make_unique( - std::move(scanner_broker_), - std::move(fast_pair_repository_)); - mediator_->StartScanning(); +TEST_F(MediatorTest, StartScanningFoundDevice) { + absl::Notification done; + EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).WillOnce([&done] { + done.Notify(); + }); + + // Create Fast Pair Mediator + mediator_ = + std::make_unique(std::move(mediums_), std::move(ui_broker_), + std::move(notification_controller_), + std::move(repository_), std::move(executor_)); + + // Create Advertiser and startAdvertising + Mediums mediums_advertiser; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_advertiser.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + mediator_->StartScanning(); + done.WaitForNotification(); +} + +TEST_F(MediatorTest, StartScanningFoundDifferentDeviceWhenDisplaying) { + absl::Notification done; + EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { + done.Notify(); + }); + + // Create Fast Pair Mediator + mediator_ = + std::make_unique(std::move(mediums_), std::move(ui_broker_), + std::move(notification_controller_), + std::move(repository_), std::move(executor_)); + + // Create Advertiser and startAdvertising + Mediums mediums_advertiser; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_advertiser.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + // Create a different Advertiser and startAdvertising to cause confliction + Mediums mediums_advertiser2; + ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId2)}; + mediums_advertiser2.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes2, fast_pair_service_uuid); + + mediator_->StartScanning(); + done.WaitForNotification(); +} + +TEST_F(MediatorTest, StartScanningFoundSameDeviceWhenDisplaying) { + absl::Notification done; + EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { + done.Notify(); + }); + + // Create Fast Pair Mediator + mediator_ = + std::make_unique(std::move(mediums_), std::move(ui_broker_), + std::move(notification_controller_), + std::move(repository_), std::move(executor_)); + + // Create Advertiser and startAdvertising + Mediums mediums_advertiser; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_advertiser.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + // Create another same Advertiser and startAdvertising to cause confliction + Mediums mediums_advertiser2; + ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId)}; + mediums_advertiser2.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes2, fast_pair_service_uuid); + + mediator_->StartScanning(); + done.WaitForNotification(); +} + +TEST_F(MediatorTest, + StartScanningForSubsequentPairingFoundSameDeviceWhenDisplaying) { + absl::Notification done; + EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { + done.Notify(); + }); + + // Create Fast Pair Mediator + mediator_ = + std::make_unique(std::move(mediums_), std::move(ui_broker_), + std::move(notification_controller_), + std::move(repository_), std::move(executor_)); + + // Create Advertiser and advertising discoverable advertisement + Mediums mediums_advertiser; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_advertiser.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + // Create another same Advertiser and advertising non-discoverable + // advertisement to cause confliction + Mediums mediums_advertiser2; + std::vector bytes = FastPairServiceDataCreator::Builder() + .SetHeader(kNotDiscoverableAdvHeader) + .SetModelId(kModelId) + .AddExtraFieldHeader(kAccountKeyFilterHeader) + .AddExtraField(kAccountKeyFilter) + .AddExtraFieldHeader(kSaltHeader) + .AddExtraField(kSalt) + .Build() + ->CreateServiceData(); + ByteArray advertisement_bytes2{std::string(bytes.begin(), bytes.end())}; + mediums_advertiser2.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes2, fast_pair_service_uuid); + + mediator_->StartScanning(); + done.WaitForNotification(); +} + +TEST_F(MediatorTest, OnDiscoveryActionClicked) { + absl::Notification done; + EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(2).WillOnce([&done] { + done.Notify(); + }); + + // Create Fast Pair Mediator + mediator_ = + std::make_unique(std::move(mediums_), std::move(ui_broker_), + std::move(notification_controller_), + std::move(repository_), std::move(executor_)); + + // Create Advertiser and startAdvertising + Mediums mediums_advertiser; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_advertiser.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); + + mediator_->StartScanning(); + done.WaitForNotificationWithTimeout(kTaskWaitTimeout); + + FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); + mock_ui_broker_->NotifyDiscoveryAction(device, + DiscoveryAction::kDismissedByTimeout); + // Create another same Advertiser and startAdvertising to cause confliction + Mediums mediums_advertiser2; + ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId)}; + mediums_advertiser2.GetBle().GetMedium().StartAdvertising( + service_id, advertisement_bytes2, fast_pair_service_uuid); + + done.WaitForNotification(); } } // namespace - } // namespace fastpair } // namespace nearby