| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/p2p/filtering_network_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/stl_util.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/renderer/p2p/empty_network_manager.h" |
| #include "media/base/media_permission.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/webrtc/rtc_base/ip_address.h" |
| |
| using NetworkList = rtc::NetworkManager::NetworkList; |
| using ::testing::SizeIs; |
| |
| namespace { |
| |
| enum EventType { |
| MIC_DENIED, // Receive mic permission denied. |
| MIC_GRANTED, // Receive mic permission granted. |
| CAMERA_DENIED, // Receive camera permission denied. |
| CAMERA_GRANTED, // Receive camera permission granted. |
| START_UPDATING, // Client calls StartUpdating() on FilteringNetworkManager. |
| STOP_UPDATING, // Client calls StopUpdating() on FilteringNetworkManager. |
| MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, // MockNetworkManager has signaled |
| // networks changed event and the |
| // underlying network is replaced by |
| // a new one. |
| MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, // MockNetworkManager has signaled |
| // networks changed event but the |
| // underlying network stays the |
| // same. |
| }; |
| |
| enum ResultType { |
| NO_SIGNAL, // Do not expect SignalNetworksChanged fired. |
| SIGNAL_ENUMERATION_BLOCKED, // Expect SignalNetworksChanged and |
| // ENUMERATION_BLOCKED. |
| SIGNAL_ENUMERATION_ALLOWED, // Expect SignalNetworksChanged and |
| // ENUMERATION_ALLOWED. |
| }; |
| |
| struct TestEntry { |
| EventType event; |
| ResultType expected_result; |
| }; |
| |
| class EmptyMdnsResponder : public webrtc::MdnsResponderInterface { |
| public: |
| void CreateNameForAddress(const rtc::IPAddress& addr, |
| NameCreatedCallback callback) override { |
| NOTREACHED(); |
| } |
| void RemoveNameForAddress(const rtc::IPAddress& addr, |
| NameRemovedCallback callback) override { |
| NOTREACHED(); |
| } |
| }; |
| |
| class MockNetworkManager : public rtc::NetworkManagerBase { |
| public: |
| MockNetworkManager() : mdns_responder_(new EmptyMdnsResponder()) {} |
| // Mimic the current behavior that once the first signal is sent, any future |
| // StartUpdating() will trigger another one. |
| void StartUpdating() override { |
| if (sent_first_update_) |
| SignalNetworksChanged(); |
| } |
| void StopUpdating() override {} |
| void GetNetworks(NetworkList* networks) const override { |
| networks->push_back(network_.get()); |
| } |
| |
| void SendNetworksChanged() { |
| sent_first_update_ = true; |
| SignalNetworksChanged(); |
| } |
| |
| webrtc::MdnsResponderInterface* GetMdnsResponder() const override { |
| return mdns_responder_.get(); |
| } |
| |
| void CopyAndSetNetwork(const rtc::Network& network) { |
| network_ = std::make_unique<rtc::Network>(network); |
| network_->AddIP(network_->GetBestIP()); |
| } |
| |
| private: |
| bool sent_first_update_ = false; |
| std::unique_ptr<rtc::Network> network_; |
| std::unique_ptr<EmptyMdnsResponder> mdns_responder_; |
| }; |
| |
| class MockMediaPermission : public media::MediaPermission { |
| public: |
| MockMediaPermission() {} |
| ~MockMediaPermission() override {} |
| |
| void RequestPermission( |
| Type type, |
| const PermissionStatusCB& permission_status_cb) override { |
| NOTIMPLEMENTED(); |
| } |
| |
| void HasPermission(Type type, |
| const PermissionStatusCB& permission_status_cb) override { |
| if (type == MediaPermission::AUDIO_CAPTURE) { |
| DCHECK(mic_callback_.is_null()); |
| mic_callback_ = permission_status_cb; |
| } else { |
| DCHECK(type == MediaPermission::VIDEO_CAPTURE); |
| DCHECK(camera_callback_.is_null()); |
| camera_callback_ = permission_status_cb; |
| } |
| } |
| |
| bool IsEncryptedMediaEnabled() override { return true; } |
| |
| void SetMicPermission(bool granted) { |
| if (mic_callback_.is_null()) |
| return; |
| |
| mic_callback_.Run(granted); |
| mic_callback_ = PermissionStatusCB(); |
| } |
| |
| void SetCameraPermission(bool granted) { |
| if (camera_callback_.is_null()) |
| return; |
| |
| camera_callback_.Run(granted); |
| camera_callback_ = PermissionStatusCB(); |
| } |
| |
| private: |
| PermissionStatusCB mic_callback_; |
| PermissionStatusCB camera_callback_; |
| }; |
| |
| } // namespace |
| |
| namespace content { |
| |
| class FilteringNetworkManagerTest : public testing::Test, |
| public sigslot::has_slots<> { |
| public: |
| FilteringNetworkManagerTest() |
| : media_permission_(new MockMediaPermission()), |
| task_runner_(new base::TestSimpleTaskRunner()), |
| task_runner_handle_(task_runner_) { |
| networks_.emplace_back("test_eth0", "Test Network Adapter 1", |
| rtc::IPAddress(0x12345600U), 24, |
| rtc::ADAPTER_TYPE_ETHERNET), |
| networks_.back().AddIP(rtc::IPAddress(0x12345678)); |
| networks_.emplace_back("test_eth1", "Test Network Adapter 2", |
| rtc::IPAddress(0x87654300U), 24, |
| rtc::ADAPTER_TYPE_ETHERNET), |
| networks_.back().AddIP(rtc::IPAddress(0x87654321)); |
| } |
| |
| void SetupNetworkManager(bool multiple_routes_requested) { |
| base_network_manager_ = std::make_unique<MockNetworkManager>(); |
| SetNewNetworkForBaseNetworkManager(); |
| if (multiple_routes_requested) { |
| network_manager_ = std::make_unique<FilteringNetworkManager>( |
| base_network_manager_.get(), GURL(), media_permission_.get()); |
| network_manager_->Initialize(); |
| } else { |
| network_manager_ = |
| std::make_unique<EmptyNetworkManager>(base_network_manager_.get()); |
| } |
| network_manager_->SignalNetworksChanged.connect( |
| this, &FilteringNetworkManagerTest::OnNetworksChanged); |
| } |
| |
| void RunTests(TestEntry* tests, size_t size) { |
| for (size_t i = 0; i < size; ++i) { |
| EXPECT_EQ(tests[i].expected_result, ProcessEvent(tests[i].event)) |
| << " in step: " << i; |
| } |
| } |
| |
| void SetNewNetworkForBaseNetworkManager() { |
| base_network_manager_->CopyAndSetNetwork(networks_[next_new_network_id_]); |
| next_new_network_id_ = (next_new_network_id_ + 1) % networks_.size(); |
| } |
| |
| ResultType ProcessEvent(EventType event) { |
| clear_callback_called(); |
| switch (event) { |
| case MIC_DENIED: |
| case MIC_GRANTED: |
| media_permission_->SetMicPermission(event == MIC_GRANTED); |
| break; |
| case CAMERA_DENIED: |
| case CAMERA_GRANTED: |
| media_permission_->SetCameraPermission(event == CAMERA_GRANTED); |
| break; |
| case START_UPDATING: |
| network_manager_->StartUpdating(); |
| break; |
| case STOP_UPDATING: |
| network_manager_->StopUpdating(); |
| break; |
| case MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK: |
| SetNewNetworkForBaseNetworkManager(); |
| base_network_manager_->SendNetworksChanged(); |
| break; |
| case MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK: |
| base_network_manager_->SendNetworksChanged(); |
| break; |
| } |
| |
| task_runner_->RunUntilIdle(); |
| |
| if (!callback_called_) |
| return NO_SIGNAL; |
| |
| if (network_manager_->enumeration_permission() == |
| rtc::NetworkManager::ENUMERATION_BLOCKED) { |
| EXPECT_EQ(0u, GetP2PNetworkList().size()); |
| return SIGNAL_ENUMERATION_BLOCKED; |
| } |
| EXPECT_EQ(1u, GetP2PNetworkList().size()); |
| return SIGNAL_ENUMERATION_ALLOWED; |
| } |
| |
| protected: |
| const NetworkList& GetP2PNetworkList() { |
| network_list_.clear(); |
| network_manager_->GetNetworks(&network_list_); |
| return network_list_; |
| } |
| |
| void OnNetworksChanged() { callback_called_ = true; } |
| void clear_callback_called() { callback_called_ = false; } |
| |
| bool callback_called_ = false; |
| std::unique_ptr<rtc::NetworkManager> network_manager_; |
| std::unique_ptr<MockNetworkManager> base_network_manager_; |
| |
| std::unique_ptr<MockMediaPermission> media_permission_; |
| |
| std::vector<rtc::Network> networks_; |
| int next_new_network_id_ = 0; |
| |
| NetworkList network_list_; |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| base::ThreadTaskRunnerHandle task_runner_handle_; |
| }; |
| |
| // Test that when multiple routes is not requested, SignalNetworksChanged is |
| // fired right after the StartUpdating(). |
| TEST_F(FilteringNetworkManagerTest, MultipleRoutesNotRequested) { |
| SetupNetworkManager(false); |
| TestEntry tests[] = { |
| // Underneath network manager signals, no callback as StartUpdating() is |
| // not called. |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| // StartUpdating() is called, should receive callback as the multiple |
| // routes is not requested. |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| // Further network signal should trigger callback, since the |
| // EmptyNetworkManager always forwards the signal from the base network |
| // manager if there is any outstanding StartUpdate(); |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, SIGNAL_ENUMERATION_BLOCKED}, |
| // StartUpdating() always triggers callback after we have sent the first |
| // network update. |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // No outstanding StartUpdating(), no more signal. |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that multiple routes request is blocked and signaled right after |
| // StartUpdating() since mic/camera permissions are denied. |
| TEST_F(FilteringNetworkManagerTest, BlockMultipleRoutesByStartUpdating) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| // Both mic and camera are denied. |
| {MIC_DENIED, NO_SIGNAL}, |
| {CAMERA_DENIED, NO_SIGNAL}, |
| // Once StartUpdating() is called, signal network changed event with |
| // ENUMERATION_BLOCKED. |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| // Further network signal should not trigger callback, since the set of |
| // networks does not change after merging. |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| // Signal when observing a change after merging while there is any |
| // outstanding StartUpdate(); |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, SIGNAL_ENUMERATION_BLOCKED}, |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that multiple routes request is blocked and signaled right after |
| // last pending permission check is denied since StartUpdating() has been called |
| // previously. |
| TEST_F(FilteringNetworkManagerTest, BlockMultipleRoutesByPermissionsDenied) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| // StartUpdating() should not fire the event before we send the first |
| // update. |
| {START_UPDATING, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| {MIC_DENIED, NO_SIGNAL}, |
| // The last permission check being denied should immediately trigger the |
| // networks changed signal, since we already have an updated network list. |
| {CAMERA_DENIED, SIGNAL_ENUMERATION_BLOCKED}, |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // No outstanding StartUpdating(), no more signal. |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that after permissions have been denied, a network change signal from |
| // the internal NetworkManager is still needed before signaling a network |
| // change outwards. This is because even if network enumeration is blocked, |
| // we still want to give time to obtain the default IP addresses. |
| TEST_F(FilteringNetworkManagerTest, BlockMultipleRoutesByNetworksChanged) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| {START_UPDATING, NO_SIGNAL}, |
| {MIC_DENIED, NO_SIGNAL}, |
| {CAMERA_DENIED, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, SIGNAL_ENUMERATION_BLOCKED}, |
| {START_UPDATING, SIGNAL_ENUMERATION_BLOCKED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that multiple routes request is granted and signaled right after |
| // a pending permission check is granted since StartUpdating() has been called |
| // previously. |
| TEST_F(FilteringNetworkManagerTest, AllowMultipleRoutesByPermissionsGranted) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| {START_UPDATING, NO_SIGNAL}, |
| {MIC_DENIED, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| // Once one media type is granted, signal networks changed with |
| // ENUMERATION_ALLOWED. |
| {CAMERA_GRANTED, SIGNAL_ENUMERATION_ALLOWED}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| {START_UPDATING, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // If there is any outstanding StartUpdating(), new event from underneath |
| // network manger should trigger SignalNetworksChanged. |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // No outstanding StartUpdating(), no more signal. |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that multiple routes request is granted and signaled right after |
| // StartUpdating() since there is at least one media permission granted. |
| TEST_F(FilteringNetworkManagerTest, AllowMultipleRoutesByStartUpdating) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| {MIC_DENIED, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| {CAMERA_GRANTED, NO_SIGNAL}, |
| // StartUpdating() should signal the event with the status of permissions |
| // granted. |
| {START_UPDATING, SIGNAL_ENUMERATION_ALLOWED}, |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| {START_UPDATING, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // Signal when observing a change after merging while there is any |
| // outstanding StartUpdate(); |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| // No outstanding StartUpdating(), no more signal. |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that multiple routes request is granted and signaled right after |
| // underneath NetworkManager's SignalNetworksChanged() as at least one |
| // permission is granted and StartUpdating() has been called. |
| TEST_F(FilteringNetworkManagerTest, AllowMultipleRoutesByNetworksChanged) { |
| SetupNetworkManager(true); |
| |
| TestEntry tests[] = { |
| {START_UPDATING, NO_SIGNAL}, |
| {CAMERA_GRANTED, NO_SIGNAL}, |
| // Underneath network manager's signal networks changed should trigger |
| // SignalNetworksChanged with ENUMERATION_ALLOWED. |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, SIGNAL_ENUMERATION_ALLOWED}, |
| {MIC_DENIED, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, SIGNAL_ENUMERATION_ALLOWED}, |
| {START_UPDATING, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, SIGNAL_ENUMERATION_ALLOWED}, |
| {STOP_UPDATING, NO_SIGNAL}, |
| {MOCK_NETWORKS_CHANGED_WITH_NEW_NETWORK, NO_SIGNAL}, |
| }; |
| |
| RunTests(tests, base::size(tests)); |
| } |
| |
| // Test that the networks provided by the GetNetworks() and |
| // GetAnyAddressNetworks() are not associated with an mDNS responder if the |
| // enumeration permission is granted. |
| TEST_F(FilteringNetworkManagerTest, NullMdnsResponderAfterPermissionGranted) { |
| SetupNetworkManager(true); |
| |
| TestEntry setup_steps[] = { |
| {MOCK_NETWORKS_CHANGED_WITH_SAME_NETWORK, NO_SIGNAL}, |
| // Both mic and camera are granted. |
| {MIC_GRANTED, NO_SIGNAL}, |
| {CAMERA_GRANTED, NO_SIGNAL}, |
| // Once StartUpdating() is called, signal network changed event with |
| // ENUMERATION_ALLOWED. |
| {START_UPDATING, SIGNAL_ENUMERATION_ALLOWED}, |
| }; |
| RunTests(setup_steps, base::size(setup_steps)); |
| |
| NetworkList networks; |
| network_manager_->GetNetworks(&networks); |
| EXPECT_THAT(networks, SizeIs(1u)); |
| for (const rtc::Network* network : networks) { |
| EXPECT_EQ(nullptr, network->GetMdnsResponder()); |
| } |
| |
| networks.clear(); |
| network_manager_->GetAnyAddressNetworks(&networks); |
| EXPECT_THAT(networks, SizeIs(2u)); |
| for (const rtc::Network* network : networks) { |
| EXPECT_EQ(nullptr, network->GetMdnsResponder()); |
| } |
| } |
| |
| // Test the networks on the default routes given by GetAnyAddressNetworks() are |
| // associated with an mDNS responder if the enumeration is blocked. |
| TEST_F(FilteringNetworkManagerTest, |
| ProvideMdnsResponderForDefaultRouteAfterPermissionDenied) { |
| SetupNetworkManager(true); |
| // By default, the enumeration is blocked if we provide |media_permission_|; |
| EXPECT_EQ(rtc::NetworkManager::ENUMERATION_BLOCKED, |
| network_manager_->enumeration_permission()); |
| |
| NetworkList networks; |
| network_manager_->GetNetworks(&networks); |
| EXPECT_TRUE(networks.empty()); |
| |
| network_manager_->GetAnyAddressNetworks(&networks); |
| EXPECT_THAT(networks, SizeIs(2u)); |
| EXPECT_NE(nullptr, network_manager_->GetMdnsResponder()); |
| for (const rtc::Network* network : networks) { |
| EXPECT_EQ(network_manager_->GetMdnsResponder(), |
| network->GetMdnsResponder()); |
| } |
| } |
| |
| } // namespace content |