// Copyright (c) 2012 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 "chromeos/network/shill_property_handler.h" #include #include #include #include #include "base/bind.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/values.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_device_client.h" #include "chromeos/dbus/shill_ipconfig_client.h" #include "chromeos/dbus/shill_manager_client.h" #include "chromeos/dbus/shill_profile_client.h" #include "chromeos/dbus/shill_service_client.h" #include "dbus/object_path.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { void DoNothingWithCallStatus(DBusMethodCallStatus call_status) { } void ErrorCallbackFunction(const std::string& error_name, const std::string& error_message) { LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message; } class TestListener : public internal::ShillPropertyHandler::Listener { public: TestListener() : technology_list_updates_(0), errors_(0) { } void UpdateManagedList(ManagedState::ManagedType type, const base::ListValue& entries) override { VLOG(1) << "UpdateManagedList[" << ManagedState::TypeToString(type) << "]: " << entries.GetSize(); UpdateEntries(GetTypeString(type), entries); } void UpdateManagedStateProperties( ManagedState::ManagedType type, const std::string& path, const base::DictionaryValue& properties) override { VLOG(2) << "UpdateManagedStateProperties: " << GetTypeString(type); initial_property_updates(GetTypeString(type))[path] += 1; } void ProfileListChanged() override {} void UpdateNetworkServiceProperty(const std::string& service_path, const std::string& key, const base::Value& value) override { AddPropertyUpdate(shill::kServiceCompleteListProperty, service_path); } void UpdateDeviceProperty(const std::string& device_path, const std::string& key, const base::Value& value) override { AddPropertyUpdate(shill::kDevicesProperty, device_path); } void UpdateIPConfigProperties( ManagedState::ManagedType type, const std::string& path, const std::string& ip_config_path, const base::DictionaryValue& properties) override { AddPropertyUpdate(shill::kIPConfigsProperty, ip_config_path); } void TechnologyListChanged() override { VLOG(1) << "TechnologyListChanged."; ++technology_list_updates_; } void CheckPortalListChanged(const std::string& check_portal_list) override {} void ManagedStateListChanged(ManagedState::ManagedType type) override { VLOG(1) << "ManagedStateListChanged: " << GetTypeString(type); AddStateListUpdate(GetTypeString(type)); } void DefaultNetworkServiceChanged(const std::string& service_path) override {} std::vector& entries(const std::string& type) { return entries_[type]; } std::map& property_updates(const std::string& type) { return property_updates_[type]; } std::map& initial_property_updates( const std::string& type) { return initial_property_updates_[type]; } int list_updates(const std::string& type) { return list_updates_[type]; } int technology_list_updates() { return technology_list_updates_; } void reset_list_updates() { VLOG(1) << "=== RESET LIST UPDATES ==="; list_updates_.clear(); technology_list_updates_ = 0; } int errors() { return errors_; } private: std::string GetTypeString(ManagedState::ManagedType type) { if (type == ManagedState::MANAGED_TYPE_NETWORK) return shill::kServiceCompleteListProperty; if (type == ManagedState::MANAGED_TYPE_DEVICE) return shill::kDevicesProperty; NOTREACHED(); return std::string(); } void UpdateEntries(const std::string& type, const base::ListValue& entries) { if (type.empty()) return; entries_[type].clear(); for (base::ListValue::const_iterator iter = entries.begin(); iter != entries.end(); ++iter) { std::string path; if ((*iter)->GetAsString(&path)) entries_[type].push_back(path); } } void AddPropertyUpdate(const std::string& type, const std::string& path) { DCHECK(!type.empty()); VLOG(2) << "AddPropertyUpdate: " << type; property_updates(type)[path] += 1; } void AddStateListUpdate(const std::string& type) { DCHECK(!type.empty()); list_updates_[type] += 1; } // Map of list-type -> paths std::map > entries_; // Map of list-type -> map of paths -> update counts std::map > property_updates_; std::map > initial_property_updates_; // Map of list-type -> list update counts std::map list_updates_; int technology_list_updates_; int errors_; }; } // namespace class ShillPropertyHandlerTest : public testing::Test { public: ShillPropertyHandlerTest() : manager_test_(NULL), device_test_(NULL), service_test_(NULL), profile_test_(NULL) { } ~ShillPropertyHandlerTest() override {} void SetUp() override { // Initialize DBusThreadManager with a stub implementation. DBusThreadManager::Initialize(); // Get the test interface for manager / device / service and clear the // default stub properties. manager_test_ = DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface(); ASSERT_TRUE(manager_test_); device_test_ = DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface(); ASSERT_TRUE(device_test_); service_test_ = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); ASSERT_TRUE(service_test_); profile_test_ = DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); ASSERT_TRUE(profile_test_); SetupShillPropertyHandler(); message_loop_.RunUntilIdle(); } void TearDown() override { shill_property_handler_.reset(); listener_.reset(); DBusThreadManager::Shutdown(); } void AddDevice(const std::string& type, const std::string& id) { ASSERT_TRUE(IsValidType(type)); device_test_->AddDevice(id, type, id); } void RemoveDevice(const std::string& id) { device_test_->RemoveDevice(id); } void AddService(const std::string& type, const std::string& id, const std::string& state) { VLOG(2) << "AddService: " << type << ": " << id << ": " << state; ASSERT_TRUE(IsValidType(type)); service_test_->AddService(id /* service_path */, id /* guid */, id /* name */, type, state, true /* visible */); } void AddServiceWithIPConfig(const std::string& type, const std::string& id, const std::string& state, const std::string& ipconfig_path) { ASSERT_TRUE(IsValidType(type)); service_test_->AddServiceWithIPConfig(id, /* service_path */ id /* guid */, id /* name */, type, state, ipconfig_path, true /* visible */); } void AddServiceToProfile(const std::string& type, const std::string& id, bool visible) { service_test_->AddService(id /* service_path */, id /* guid */, id /* name */, type, shill::kStateIdle, visible); std::vector profiles; profile_test_->GetProfilePaths(&profiles); ASSERT_TRUE(profiles.size() > 0); base::DictionaryValue properties; // Empty entry profile_test_->AddService(profiles[0], id); } void RemoveService(const std::string& id) { service_test_->RemoveService(id); } // Call this after any initial Shill client setup void SetupShillPropertyHandler() { SetupDefaultShillState(); listener_.reset(new TestListener); shill_property_handler_.reset( new internal::ShillPropertyHandler(listener_.get())); shill_property_handler_->Init(); } bool IsValidType(const std::string& type) { return (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap || type == shill::kTypeWifi || type == shill::kTypeWimax || type == shill::kTypeBluetooth || type == shill::kTypeCellular || type == shill::kTypeVPN); } protected: void SetupDefaultShillState() { message_loop_.RunUntilIdle(); // Process any pending updates device_test_->ClearDevices(); AddDevice(shill::kTypeWifi, "stub_wifi_device1"); AddDevice(shill::kTypeCellular, "stub_cellular_device1"); service_test_->ClearServices(); AddService(shill::kTypeEthernet, "stub_ethernet", shill::kStateOnline); AddService(shill::kTypeWifi, "stub_wifi1", shill::kStateOnline); AddService(shill::kTypeWifi, "stub_wifi2", shill::kStateIdle); AddService(shill::kTypeCellular, "stub_cellular1", shill::kStateIdle); } base::MessageLoopForUI message_loop_; scoped_ptr listener_; scoped_ptr shill_property_handler_; ShillManagerClient::TestInterface* manager_test_; ShillDeviceClient::TestInterface* device_test_; ShillServiceClient::TestInterface* service_test_; ShillProfileClient::TestInterface* profile_test_; private: DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest); }; TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) { EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(shill::kTypeWifi)); EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi)); const size_t kNumShillManagerClientStubImplDevices = 2; EXPECT_EQ(kNumShillManagerClientStubImplDevices, listener_->entries(shill::kDevicesProperty).size()); const size_t kNumShillManagerClientStubImplServices = 4; EXPECT_EQ(kNumShillManagerClientStubImplServices, listener_->entries(shill::kServiceCompleteListProperty).size()); EXPECT_EQ(0, listener_->errors()); } TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) { const int initial_technology_updates = 2; // Available and Enabled lists EXPECT_EQ(initial_technology_updates, listener_->technology_list_updates()); // Remove an enabled technology. Updates both the Available and Enabled lists. listener_->reset_list_updates(); manager_test_->RemoveTechnology(shill::kTypeWifi); message_loop_.RunUntilIdle(); EXPECT_EQ(2, listener_->technology_list_updates()); // Add a disabled technology. listener_->reset_list_updates(); manager_test_->AddTechnology(shill::kTypeWifi, false); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->technology_list_updates()); EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable( shill::kTypeWifi)); EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi)); // Enable the technology. listener_->reset_list_updates(); DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology( shill::kTypeWifi, base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->technology_list_updates()); EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi)); EXPECT_EQ(0, listener_->errors()); } TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) { const size_t kNumShillManagerClientStubImplDevices = 2; EXPECT_EQ(kNumShillManagerClientStubImplDevices, listener_->entries(shill::kDevicesProperty).size()); // Add a device. listener_->reset_list_updates(); const std::string kTestDevicePath("test_wifi_device1"); AddDevice(shill::kTypeWifi, kTestDevicePath); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kDevicesProperty)); EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1, listener_->entries(shill::kDevicesProperty).size()); // Remove a device listener_->reset_list_updates(); RemoveDevice(kTestDevicePath); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kDevicesProperty)); EXPECT_EQ(kNumShillManagerClientStubImplDevices, listener_->entries(shill::kDevicesProperty).size()); EXPECT_EQ(0, listener_->errors()); } TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) { const size_t kNumShillManagerClientStubImplServices = 4; EXPECT_EQ(kNumShillManagerClientStubImplServices, listener_->entries(shill::kServiceCompleteListProperty).size()); // Add a service. listener_->reset_list_updates(); const std::string kTestServicePath("test_wifi_service1"); AddService(shill::kTypeWifi, kTestServicePath, shill::kStateIdle); message_loop_.RunUntilIdle(); // Add should trigger a service list update and update entries. EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); EXPECT_EQ(kNumShillManagerClientStubImplServices + 1, listener_->entries(shill::kServiceCompleteListProperty).size()); // Service receives an initial property update. EXPECT_EQ(1, listener_->initial_property_updates( shill::kServiceCompleteListProperty)[kTestServicePath]); // Change a property. base::FundamentalValue scan_interval(3); DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(kTestServicePath), shill::kScanIntervalProperty, scan_interval, base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); // Property change triggers an update (but not a service list update). EXPECT_EQ(1, listener_->property_updates( shill::kServiceCompleteListProperty)[kTestServicePath]); // Change the visibility of a service. This will trigger a service list // updates. listener_->reset_list_updates(); DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(kTestServicePath), shill::kVisibleProperty, base::FundamentalValue(false), base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); // Remove a service. This will update the entries and signal a service list // update. listener_->reset_list_updates(); RemoveService(kTestServicePath); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); EXPECT_EQ(kNumShillManagerClientStubImplServices, listener_->entries(shill::kServiceCompleteListProperty).size()); EXPECT_EQ(0, listener_->errors()); } TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerIPConfigPropertyChanged) { // Set the properties for an IP Config object. const std::string kTestIPConfigPath("test_ip_config_path"); base::StringValue ip_address("192.168.1.1"); DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( dbus::ObjectPath(kTestIPConfigPath), shill::kAddressProperty, ip_address, base::Bind(&DoNothingWithCallStatus)); base::ListValue dns_servers; dns_servers.Append(new base::StringValue("192.168.1.100")); dns_servers.Append(new base::StringValue("192.168.1.101")); DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( dbus::ObjectPath(kTestIPConfigPath), shill::kNameServersProperty, dns_servers, base::Bind(&DoNothingWithCallStatus)); base::FundamentalValue prefixlen(8); DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( dbus::ObjectPath(kTestIPConfigPath), shill::kPrefixlenProperty, prefixlen, base::Bind(&DoNothingWithCallStatus)); base::StringValue gateway("192.0.0.1"); DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( dbus::ObjectPath(kTestIPConfigPath), shill::kGatewayProperty, gateway, base::Bind(&DoNothingWithCallStatus)); message_loop_.RunUntilIdle(); // Add a service with an empty ipconfig and then update // its ipconfig property. const std::string kTestServicePath1("test_wifi_service1"); AddService(shill::kTypeWifi, kTestServicePath1, shill::kStateIdle); message_loop_.RunUntilIdle(); // This is the initial property update. EXPECT_EQ(1, listener_->initial_property_updates( shill::kServiceCompleteListProperty)[kTestServicePath1]); DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(kTestServicePath1), shill::kIPConfigProperty, base::StringValue(kTestIPConfigPath), base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); // IPConfig property change on the service should trigger an IPConfigs update. EXPECT_EQ(1, listener_->property_updates( shill::kIPConfigsProperty)[kTestIPConfigPath]); // Now, Add a new service with the IPConfig already set. const std::string kTestServicePath2("test_wifi_service2"); AddServiceWithIPConfig(shill::kTypeWifi, kTestServicePath2, shill::kStateIdle, kTestIPConfigPath); message_loop_.RunUntilIdle(); // A service with the IPConfig property already set should trigger an // additional IPConfigs update. EXPECT_EQ(2, listener_->property_updates( shill::kIPConfigsProperty)[kTestIPConfigPath]); } TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServiceList) { // Add an entry to the profile only. const std::string kTestServicePath1("stub_wifi_profile_only1"); AddServiceToProfile(shill::kTypeWifi, kTestServicePath1, false /* visible */); message_loop_.RunUntilIdle(); // Update the Manager properties. This should trigger a single list update, // an initial property update, and a regular property update. listener_->reset_list_updates(); shill_property_handler_->UpdateManagerProperties(); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); EXPECT_EQ(1, listener_->initial_property_updates( shill::kServiceCompleteListProperty)[kTestServicePath1]); EXPECT_EQ(1, listener_->property_updates( shill::kServiceCompleteListProperty)[kTestServicePath1]); // Add a new entry to the services and the profile; should also trigger a // service list update, and a property update. listener_->reset_list_updates(); const std::string kTestServicePath2("stub_wifi_profile_only2"); AddServiceToProfile(shill::kTypeWifi, kTestServicePath2, true); shill_property_handler_->UpdateManagerProperties(); message_loop_.RunUntilIdle(); EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); EXPECT_EQ(1, listener_->initial_property_updates( shill::kServiceCompleteListProperty)[kTestServicePath2]); EXPECT_EQ(1, listener_->property_updates( shill::kServiceCompleteListProperty)[kTestServicePath2]); } TEST_F(ShillPropertyHandlerTest, ProhibitedTechnologies) { std::vector prohibited_technologies; prohibited_technologies.push_back(shill::kTypeEthernet); EXPECT_TRUE( shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet)); shill_property_handler_->SetProhibitedTechnologies( prohibited_technologies, network_handler::ErrorCallback()); message_loop_.RunUntilIdle(); // Disabled EXPECT_FALSE( shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet)); // Can not enable it back shill_property_handler_->SetTechnologyEnabled( shill::kTypeEthernet, true, network_handler::ErrorCallback()); message_loop_.RunUntilIdle(); EXPECT_FALSE( shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet)); // Can enable it back after policy changes prohibited_technologies.clear(); shill_property_handler_->SetProhibitedTechnologies( prohibited_technologies, network_handler::ErrorCallback()); shill_property_handler_->SetTechnologyEnabled( shill::kTypeEthernet, true, network_handler::ErrorCallback()); message_loop_.RunUntilIdle(); EXPECT_TRUE( shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet)); } } // namespace chromeos