| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/nearby/nearby_process_manager_impl.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "ash/public/cpp/network_config_service.h" |
| #include "base/files/file_path.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "base/timer/mock_timer.h" |
| #include "chrome/browser/ash/nearby/bluetooth_adapter_manager.h" |
| #include "chrome/browser/ash/nearby/nearby_dependencies_provider.h" |
| #include "chrome/browser/ash/nearby/nearby_process_manager_factory.h" |
| #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/services/sharing/nearby/test_support/fake_adapter.h" |
| #include "chrome/services/sharing/nearby/test_support/fake_nearby_presence_credential_storage.h" |
| #include "chrome/services/sharing/nearby/test_support/mock_webrtc_dependencies.h" |
| #include "chromeos/ash/services/nearby/public/cpp/fake_firewall_hole_factory.h" |
| #include "chromeos/ash/services/nearby/public/cpp/fake_mdns_manager.h" |
| #include "chromeos/ash/services/nearby/public/cpp/fake_nearby_presence.h" |
| #include "chromeos/ash/services/nearby/public/cpp/fake_tcp_socket_factory.h" |
| #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_connections.h" |
| #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_sharing_decoder.h" |
| #include "chromeos/ash/services/nearby/public/cpp/mock_quick_start_decoder.h" |
| #include "chromeos/ash/services/nearby/public/mojom/firewall_hole.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/mdns.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/nearby_connections.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/nearby_decoder.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/sharing.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h" |
| #include "chromeos/ash/services/nearby/public/mojom/webrtc.mojom.h" |
| #include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h" |
| #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" |
| #include "components/account_id/account_id.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_utils.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "device/bluetooth/test/mock_bluetooth_adapter.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| // Noop implementation for tests. |
| class FakeWifiDirectManager |
| : public ash::wifi_direct::mojom::WifiDirectManager { |
| // ash::wifi_direct::mojom::WifiDirectManager |
| void CreateWifiDirectGroup( |
| ash::wifi_direct::mojom::WifiCredentialsPtr credentials, |
| CreateWifiDirectGroupCallback callback) override { |
| // Noop |
| } |
| void ConnectToWifiDirectGroup( |
| ash::wifi_direct::mojom::WifiCredentialsPtr credentials, |
| std::optional<uint32_t> frequency, |
| ConnectToWifiDirectGroupCallback callback) override { |
| // Noop |
| } |
| void GetWifiP2PCapabilities( |
| GetWifiP2PCapabilitiesCallback callback) override { |
| // Noop |
| } |
| }; |
| |
| } // namespace |
| |
| namespace ash { |
| namespace nearby { |
| namespace { |
| |
| class FakeSharingMojoService : public sharing::mojom::Sharing { |
| public: |
| FakeSharingMojoService() = default; |
| ~FakeSharingMojoService() override = default; |
| |
| bool AreMocksSet() const { return mock_connections_ && mock_decoder_; } |
| |
| mojo::PendingRemote<sharing::mojom::Sharing> BindSharingService() { |
| return receiver_.BindNewPipeAndPassRemote(); |
| } |
| |
| void Reset() { |
| receiver_.reset(); |
| mock_connections_.reset(); |
| mock_decoder_.reset(); |
| mock_quick_start_decoder_.reset(); |
| } |
| |
| private: |
| // mojom::Sharing: |
| void Connect( |
| sharing::mojom::NearbyDependenciesPtr deps, |
| mojo::PendingReceiver<NearbyConnectionsMojom> connections_receiver, |
| mojo::PendingReceiver<NearbyPresenceMojom> presence_receiver, |
| mojo::PendingReceiver<sharing::mojom::NearbySharingDecoder> |
| decoder_receiver, |
| mojo::PendingReceiver<ash::quick_start::mojom::QuickStartDecoder> |
| quick_start_decoder_receiver) override { |
| EXPECT_FALSE(mock_connections_); |
| EXPECT_FALSE(fake_presence_); |
| EXPECT_FALSE(mock_decoder_); |
| EXPECT_FALSE(mock_quick_start_decoder_); |
| |
| mock_connections_ = std::make_unique<MockNearbyConnections>(); |
| mock_connections_->BindInterface(std::move(connections_receiver)); |
| |
| fake_presence_ = std::make_unique<presence::FakeNearbyPresence>(); |
| fake_presence_->BindInterface(std::move(presence_receiver)); |
| |
| mock_decoder_ = std::make_unique<MockNearbySharingDecoder>(); |
| mock_decoder_->BindInterface(std::move(decoder_receiver)); |
| |
| mock_quick_start_decoder_ = std::make_unique<MockQuickStartDecoder>(); |
| mock_quick_start_decoder_->BindInterface( |
| std::move(quick_start_decoder_receiver)); |
| } |
| |
| void ShutDown(ShutDownCallback callback) override { |
| mock_connections_.reset(); |
| fake_presence_.reset(); |
| mock_decoder_.reset(); |
| mock_quick_start_decoder_.reset(); |
| std::move(callback).Run(); |
| } |
| |
| std::unique_ptr<MockNearbyConnections> mock_connections_; |
| std::unique_ptr<presence::FakeNearbyPresence> fake_presence_; |
| std::unique_ptr<MockNearbySharingDecoder> mock_decoder_; |
| std::unique_ptr<MockQuickStartDecoder> mock_quick_start_decoder_; |
| mojo::Receiver<sharing::mojom::Sharing> receiver_{this}; |
| }; |
| |
| } // namespace |
| |
| class NearbyProcessManagerImplTest : public testing::Test { |
| public: |
| class FakeNearbyDependenciesProvider : public NearbyDependenciesProvider { |
| public: |
| FakeNearbyDependenciesProvider() = default; |
| ~FakeNearbyDependenciesProvider() override = default; |
| |
| // NearbyDependenciesProvider: |
| sharing::mojom::NearbyDependenciesPtr GetDependencies() override { |
| fake_adapter_ = std::make_unique<bluetooth::FakeAdapter>(); |
| fake_nearby_presence_credential_storage_ = |
| std::make_unique<presence::FakeNearbyPresenceCredentialStorage>(); |
| webrtc_dependencies_ = |
| std::make_unique<sharing::MockWebRtcDependencies>(); |
| |
| // Set up CrosNetworkConfig mojo service. |
| mojo::PendingRemote<chromeos::network_config::mojom::CrosNetworkConfig> |
| cros_network_config_remote; |
| ash::GetNetworkConfigService( |
| cros_network_config_remote.InitWithNewPipeAndPassReceiver()); |
| |
| // Set up firewall hole factory mojo service. |
| mojo::PendingRemote<sharing::mojom::FirewallHoleFactory> |
| firewall_hole_factory_remote; |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<ash::nearby::FakeFirewallHoleFactory>(), |
| firewall_hole_factory_remote.InitWithNewPipeAndPassReceiver()); |
| |
| // Set up TCP socket factory mojo service. |
| mojo::PendingRemote<sharing::mojom::TcpSocketFactory> |
| tcp_socket_factory_remote; |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<ash::nearby::FakeTcpSocketFactory>( |
| /*default_local_addr=*/net::IPEndPoint( |
| net::IPAddress(192, 168, 86, 75), 44444)), |
| tcp_socket_factory_remote.InitWithNewPipeAndPassReceiver()); |
| |
| // Set up Mdns Manager mojo service. |
| mojo::PendingRemote<sharing::mojom::MdnsManager> mdns_manager_remote; |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<ash::nearby::FakeMdnsManager>(), |
| mdns_manager_remote.InitWithNewPipeAndPassReceiver()); |
| |
| // Set up fake WiFiDirect mojo services. |
| mojo::PendingRemote<ash::wifi_direct::mojom::WifiDirectManager> |
| wifi_direct_manager_remote; |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<FakeWifiDirectManager>(), |
| wifi_direct_manager_remote.InitWithNewPipeAndPassReceiver()); |
| mojo::PendingRemote<::sharing::mojom::FirewallHoleFactory> |
| wifi_direct_firewall_hole_factory_remote; |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<ash::nearby::FakeFirewallHoleFactory>(), |
| wifi_direct_firewall_hole_factory_remote |
| .InitWithNewPipeAndPassReceiver()); |
| |
| return sharing::mojom::NearbyDependencies::New( |
| fake_adapter_->adapter_.BindNewPipeAndPassRemote(), |
| sharing::mojom::WebRtcDependencies::New( |
| webrtc_dependencies_->socket_manager_.BindNewPipeAndPassRemote(), |
| webrtc_dependencies_->mdns_responder_factory_ |
| .BindNewPipeAndPassRemote(), |
| webrtc_dependencies_->ice_config_fetcher_ |
| .BindNewPipeAndPassRemote(), |
| webrtc_dependencies_->messenger_.BindNewPipeAndPassRemote()), |
| sharing::mojom::WifiLanDependencies::New( |
| std::move(cros_network_config_remote), |
| std::move(firewall_hole_factory_remote), |
| std::move(tcp_socket_factory_remote), |
| std::move(mdns_manager_remote)), |
| ::sharing::mojom::WifiDirectDependencies::New( |
| std::move(wifi_direct_manager_remote), |
| std::move(wifi_direct_firewall_hole_factory_remote)), |
| fake_nearby_presence_credential_storage_->receiver() |
| .BindNewPipeAndPassRemote(), |
| ::nearby::api::LogMessage::Severity::kInfo); |
| } |
| |
| void PrepareForShutdown() override { prepare_for_shutdown_count_++; } |
| |
| int prepare_for_shutdown_count() { return prepare_for_shutdown_count_; } |
| |
| private: |
| network_config::CrosNetworkConfigTestHelper |
| cros_network_config_test_helper_; |
| std::unique_ptr<bluetooth::FakeAdapter> fake_adapter_; |
| std::unique_ptr<presence::FakeNearbyPresenceCredentialStorage> |
| fake_nearby_presence_credential_storage_; |
| std::unique_ptr<sharing::MockWebRtcDependencies> webrtc_dependencies_; |
| int prepare_for_shutdown_count_ = 0; |
| }; |
| |
| NearbyProcessManagerImplTest() = default; |
| ~NearbyProcessManagerImplTest() override = default; |
| |
| // testing::Test: |
| void SetUp() override { |
| auto mock_timer = std::make_unique<base::MockOneShotTimer>(); |
| mock_timer_ = mock_timer.get(); |
| |
| nearby_process_manager_ = base::WrapUnique(new NearbyProcessManagerImpl( |
| &fake_deps_provider_, std::move(mock_timer), |
| base::BindRepeating(&FakeSharingMojoService::BindSharingService, |
| base::Unretained(&fake_sharing_mojo_service_)))); |
| } |
| |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> |
| CreateReference() { |
| auto reference = nearby_process_manager_->GetNearbyProcessReference( |
| base::BindOnce(&NearbyProcessManagerImplTest::OnProcessStopped, |
| base::Unretained(this))); |
| GetImpl()->sharing_.FlushForTesting(); |
| return reference; |
| } |
| |
| FakeSharingMojoService* fake_sharing_mojo_service() { |
| return &fake_sharing_mojo_service_; |
| } |
| |
| size_t num_process_stopped_calls() const { |
| return num_process_stopped_calls_; |
| } |
| |
| FakeNearbyDependenciesProvider* fake_deps_provider() { |
| return &fake_deps_provider_; |
| } |
| |
| void VerifyBound( |
| const NearbyProcessManager::NearbyProcessReference* reference) { |
| EXPECT_TRUE(GetImpl()->sharing_.is_bound()); |
| EXPECT_TRUE(reference->GetNearbyConnections().is_bound()); |
| EXPECT_TRUE(reference->GetNearbyPresence().is_bound()); |
| EXPECT_TRUE(reference->GetNearbySharingDecoder().is_bound()); |
| EXPECT_TRUE(reference->GetQuickStartDecoder().is_bound()); |
| EXPECT_TRUE(fake_sharing_mojo_service_.AreMocksSet()); |
| } |
| |
| void VerifyNotBound() { |
| EXPECT_FALSE(GetImpl()->sharing_.is_bound()); |
| EXPECT_FALSE(fake_sharing_mojo_service_.AreMocksSet()); |
| } |
| |
| bool IsTimerRunning() const { return mock_timer_->IsRunning(); } |
| |
| void FireTimer() { mock_timer_->Fire(); } |
| |
| private: |
| NearbyProcessManagerImpl* GetImpl() { |
| return static_cast<NearbyProcessManagerImpl*>( |
| nearby_process_manager_.get()); |
| } |
| |
| void OnProcessStopped(NearbyProcessManager::NearbyProcessShutdownReason) { |
| ++num_process_stopped_calls_; |
| } |
| |
| const base::test::TaskEnvironment task_environment_; |
| size_t num_process_stopped_calls_ = 0u; |
| |
| FakeSharingMojoService fake_sharing_mojo_service_; |
| FakeNearbyDependenciesProvider fake_deps_provider_; |
| |
| std::unique_ptr<NearbyProcessManager> nearby_process_manager_; |
| |
| raw_ptr<base::MockOneShotTimer> mock_timer_ = nullptr; |
| }; |
| |
| TEST_F(NearbyProcessManagerImplTest, StartAndStop) { |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> reference = |
| CreateReference(); |
| VerifyBound(reference.get()); |
| |
| reference.reset(); |
| FireTimer(); |
| base::RunLoop().RunUntilIdle(); |
| VerifyNotBound(); |
| EXPECT_EQ(0u, num_process_stopped_calls()); |
| EXPECT_EQ(1, fake_deps_provider()->prepare_for_shutdown_count()); |
| |
| // Reset, then repeat process to verify it still works with multiple tries. |
| fake_sharing_mojo_service()->Reset(); |
| |
| reference = CreateReference(); |
| VerifyBound(reference.get()); |
| |
| reference.reset(); |
| FireTimer(); |
| base::RunLoop().RunUntilIdle(); |
| VerifyNotBound(); |
| EXPECT_EQ(0u, num_process_stopped_calls()); |
| EXPECT_EQ(2, fake_deps_provider()->prepare_for_shutdown_count()); |
| } |
| |
| TEST_F(NearbyProcessManagerImplTest, MultipleReferences) { |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> reference1 = |
| CreateReference(); |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> reference2 = |
| CreateReference(); |
| VerifyBound(reference1.get()); |
| VerifyBound(reference2.get()); |
| |
| // Deleting one reference should still keep the other reference bound. |
| reference1.reset(); |
| EXPECT_FALSE(IsTimerRunning()); |
| base::RunLoop().RunUntilIdle(); |
| VerifyBound(reference2.get()); |
| |
| reference2.reset(); |
| FireTimer(); |
| base::RunLoop().RunUntilIdle(); |
| VerifyNotBound(); |
| EXPECT_EQ(0u, num_process_stopped_calls()); |
| EXPECT_EQ(1, fake_deps_provider()->prepare_for_shutdown_count()); |
| } |
| |
| TEST_F(NearbyProcessManagerImplTest, ProcessStopped) { |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> reference = |
| CreateReference(); |
| VerifyBound(reference.get()); |
| |
| // Reset, then wait for the disconnection to propagate. |
| fake_sharing_mojo_service()->Reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| VerifyNotBound(); |
| EXPECT_EQ(1u, num_process_stopped_calls()); |
| EXPECT_FALSE(IsTimerRunning()); |
| } |
| |
| TEST_F(NearbyProcessManagerImplTest, |
| NewReferenceObtainedWhileWaitingToShutDown) { |
| std::unique_ptr<NearbyProcessManager::NearbyProcessReference> reference = |
| CreateReference(); |
| VerifyBound(reference.get()); |
| |
| // Delete the reference; the timer should be running so that the process is |
| // shut down after the cleanup timeout. |
| reference.reset(); |
| EXPECT_TRUE(IsTimerRunning()); |
| |
| // Obtain a new reference; the timer should have stopped. |
| reference = CreateReference(); |
| VerifyBound(reference.get()); |
| EXPECT_FALSE(IsTimerRunning()); |
| |
| // Delete the reference and let the timer fire to shut down the process. |
| reference.reset(); |
| FireTimer(); |
| base::RunLoop().RunUntilIdle(); |
| VerifyNotBound(); |
| EXPECT_EQ(0u, num_process_stopped_calls()); |
| } |
| |
| } // namespace nearby |
| } // namespace ash |