Chromium Code Reviews| Index: chromeos/network/network_configuration_handler.cc |
| diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc |
| index 1ad09d80d046c05d4a67930f1b46324ac74da084..85a79c703d9ee69b37fd397bc887d552524e84db 100644 |
| --- a/chromeos/network/network_configuration_handler.cc |
| +++ b/chromeos/network/network_configuration_handler.cc |
| @@ -20,6 +20,7 @@ |
| #include "chromeos/dbus/shill_manager_client.h" |
| #include "chromeos/dbus/shill_profile_client.h" |
| #include "chromeos/dbus/shill_service_client.h" |
| +#include "chromeos/network/network_device_handler.h" |
| #include "chromeos/network/network_event_log.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/network_state_handler.h" |
| @@ -34,8 +35,8 @@ namespace { |
| // Strip surrounding "" from keys (if present). |
| std::string StripQuotations(const std::string& in_str) { |
| size_t len = in_str.length(); |
| - if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"') |
| - return in_str.substr(1, len-2); |
| + if (len >= 2 && in_str[0] == '"' && in_str[len - 1] == '"') |
| + return in_str.substr(1, len - 2); |
| return in_str; |
| } |
| @@ -44,8 +45,8 @@ void InvokeErrorCallback(const std::string& service_path, |
| const std::string& error_name) { |
| std::string error_msg = "Config Error: " + error_name; |
| NET_LOG_ERROR(error_msg, service_path); |
| - network_handler::RunErrorCallback( |
| - error_callback, service_path, error_name, error_msg); |
| + network_handler::RunErrorCallback(error_callback, service_path, error_name, |
| + error_msg); |
| } |
| void GetPropertiesCallback( |
| @@ -57,8 +58,7 @@ void GetPropertiesCallback( |
| if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| // Because network services are added and removed frequently, we will see |
| // failures regularly, so don't log these. |
| - network_handler::RunErrorCallback(error_callback, |
| - service_path, |
| + network_handler::RunErrorCallback(error_callback, service_path, |
| network_handler::kDBusFailedError, |
| network_handler::kDBusFailedErrorMessage); |
| return; |
| @@ -82,23 +82,29 @@ void SetNetworkProfileErrorCallback( |
| const std::string& dbus_error_name, |
| const std::string& dbus_error_message) { |
| network_handler::ShillErrorCallbackFunction( |
| - "Config.SetNetworkProfile Failed: " + profile_path, |
| - service_path, error_callback, |
| - dbus_error_name, dbus_error_message); |
| + "Config.SetNetworkProfile Failed: " + profile_path, service_path, |
| + error_callback, dbus_error_name, dbus_error_message); |
| } |
| void LogConfigProperties(const std::string& desc, |
| const std::string& path, |
| const base::DictionaryValue& properties) { |
| - for (base::DictionaryValue::Iterator iter(properties); |
| - !iter.IsAtEnd(); iter.Advance()) { |
| + for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd(); |
| + iter.Advance()) { |
| std::string v = "******"; |
| if (!shill_property_util::IsPassphraseKey(iter.key())) |
| base::JSONWriter::Write(&iter.value(), &v); |
| - NET_LOG_DEBUG(desc, path + "." + iter.key() + "=" + v); |
| + NET_LOG_DEBUG(desc, path + "." + iter.key() + "=" + v); |
| } |
| } |
| +bool PropertyRequiresIPConfigRefresh(const std::string& key) { |
| + return key == shill::kStaticIPAddressProperty || |
| + key == shill::kStaticIPPrefixlenProperty || |
| + key == shill::kStaticIPGatewayProperty || |
| + key == shill::kStaticIPNameServersProperty; |
| +} |
| + |
| } // namespace |
| // Helper class to request from Shill the profile entries associated with a |
| @@ -119,12 +125,12 @@ class NetworkConfigurationHandler::ProfileEntryDeleter |
| guid_(guid), |
| source_(source), |
| callback_(callback), |
| - error_callback_(error_callback) { |
| - } |
| + error_callback_(error_callback) {} |
| void Run() { |
| - DBusThreadManager::Get()->GetShillServiceClient()-> |
| - GetLoadableProfileEntries( |
| + DBusThreadManager::Get() |
| + ->GetShillServiceClient() |
| + ->GetLoadableProfileEntries( |
| dbus::ObjectPath(service_path_), |
| base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback, |
| AsWeakPtr())); |
| @@ -135,46 +141,49 @@ class NetworkConfigurationHandler::ProfileEntryDeleter |
| DBusMethodCallStatus call_status, |
| const base::DictionaryValue& profile_entries) { |
| if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| - InvokeErrorCallback( |
| - service_path_, error_callback_, "GetLoadableProfileEntriesFailed"); |
| + InvokeErrorCallback(service_path_, error_callback_, |
| + "GetLoadableProfileEntriesFailed"); |
| // ProfileEntryDeleterCompleted will delete this. |
| owner_->ProfileEntryDeleterCompleted(service_path_, guid_, source_, |
| false /* failed */); |
| return; |
| } |
| - for (base::DictionaryValue::Iterator iter(profile_entries); |
| - !iter.IsAtEnd(); iter.Advance()) { |
| + for (base::DictionaryValue::Iterator iter(profile_entries); !iter.IsAtEnd(); |
| + iter.Advance()) { |
| std::string profile_path = StripQuotations(iter.key()); |
| std::string entry_path; |
| iter.value().GetAsString(&entry_path); |
| if (profile_path.empty() || entry_path.empty()) { |
| - NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf( |
| - "%s: %s", profile_path.c_str(), entry_path.c_str())); |
| + NET_LOG_ERROR("Failed to parse Profile Entry", |
| + base::StringPrintf("%s: %s", profile_path.c_str(), |
| + entry_path.c_str())); |
| continue; |
| } |
| if (profile_delete_entries_.count(profile_path) != 0) { |
| - NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf( |
| - "%s: %s", profile_path.c_str(), entry_path.c_str())); |
| + NET_LOG_ERROR("Multiple Profile Entries", |
| + base::StringPrintf("%s: %s", profile_path.c_str(), |
| + entry_path.c_str())); |
| continue; |
| } |
| - NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf( |
| - "%s: %s", profile_path.c_str(), entry_path.c_str())); |
| + NET_LOG_DEBUG("Delete Profile Entry", |
| + base::StringPrintf("%s: %s", profile_path.c_str(), |
| + entry_path.c_str())); |
| profile_delete_entries_[profile_path] = entry_path; |
| DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( |
| - dbus::ObjectPath(profile_path), |
| - entry_path, |
| + dbus::ObjectPath(profile_path), entry_path, |
| base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback, |
| AsWeakPtr(), profile_path, entry_path), |
| - base::Bind(&ProfileEntryDeleter::ShillErrorCallback, |
| - AsWeakPtr(), profile_path, entry_path)); |
| + base::Bind(&ProfileEntryDeleter::ShillErrorCallback, AsWeakPtr(), |
| + profile_path, entry_path)); |
| } |
| } |
| void ProfileEntryDeletedCallback(const std::string& profile_path, |
| const std::string& entry) { |
| - NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf( |
| - "%s: %s", profile_path.c_str(), entry.c_str())); |
| + NET_LOG_DEBUG( |
| + "Profile Entry Deleted", |
| + base::StringPrintf("%s: %s", profile_path.c_str(), entry.c_str())); |
| profile_delete_entries_.erase(profile_path); |
| if (!profile_delete_entries_.empty()) |
| return; |
| @@ -232,8 +241,8 @@ void NetworkConfigurationHandler::GetProperties( |
| NET_LOG_USER("GetProperties", service_path); |
| DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( |
| dbus::ObjectPath(service_path), |
| - base::Bind(&GetPropertiesCallback, |
| - callback, error_callback, service_path)); |
| + base::Bind(&GetPropertiesCallback, callback, error_callback, |
| + service_path)); |
| } |
| void NetworkConfigurationHandler::SetProperties( |
| @@ -248,16 +257,48 @@ void NetworkConfigurationHandler::SetProperties( |
| return; |
| } |
| NET_LOG_USER("SetProperties", service_path); |
| - LogConfigProperties("SetProperty", service_path, properties); |
| + // Copy |properties| to |properties_to_set| then iterate over |properties| and |
| + // move any NULL properties to |properties_to_clear|, removing them from |
| + // |properties_to_set|. |
| + scoped_ptr<base::DictionaryValue> properties_to_set(properties.DeepCopy()); |
| + std::vector<std::string> properties_to_clear; |
| + for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd(); |
| + iter.Advance()) { |
| + const std::string& key = iter.key(); |
| + const base::Value& value = iter.value(); |
| + if (value.IsType(base::Value::TYPE_NULL)) { |
| + properties_to_set->RemoveWithoutPathExpansion(key, nullptr); |
| + properties_to_clear.push_back(key); |
| + } else if (value.IsType(base::Value::TYPE_DICTIONARY)) { |
|
pneubeck (no reviews)
2014/12/05 09:47:46
isn't this rather dangerous if you want to support
stevenjb
2015/01/08 00:44:39
Removed (for now at least) since we are using empt
|
| + // Also clear empty dictionaries. |
| + const base::DictionaryValue* dictionary; |
| + if (!value.GetAsDictionary(&dictionary) || dictionary->empty()) { |
|
pneubeck (no reviews)
2014/12/05 09:47:46
this check for !GetAsDictionary after IsType(Dicti
stevenjb
2015/01/08 00:44:39
Acknowledged.
|
| + properties_to_set->RemoveWithoutPathExpansion(key, nullptr); |
| + properties_to_clear.push_back(key); |
| + } |
| + } |
| + } |
| + |
| + // Copy input properties to pass to observers on completion. |
| scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy()); |
| - DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( |
| - dbus::ObjectPath(service_path), properties, |
| - base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback, |
| - AsWeakPtr(), service_path, base::Passed(&properties_copy), |
| - source, callback), |
| - base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback, |
| - AsWeakPtr(), service_path, error_callback)); |
| + |
| + if (!properties_to_clear.empty()) { |
| + // ClearProperties chains a Shill GetProperties request followed by a Shill |
| + // ClearProperties request, so wait for those to complete before setting any |
| + // properties. |
| + ClearProperties( |
|
pneubeck (no reviews)
2014/12/05 09:47:46
is this behavior covered in NCH unit tests already
stevenjb
2015/01/08 00:44:39
Removed for now.
|
| + service_path, properties_to_clear, |
| + base::Bind(&NetworkConfigurationHandler::SendSetPropertiesToShill, |
| + AsWeakPtr(), service_path, base::Passed(&properties_copy), |
| + base::Passed(&properties_to_set), source, callback, |
| + error_callback), |
| + error_callback); |
| + } else { |
| + SendSetPropertiesToShill(service_path, properties_copy.Pass(), |
| + properties_to_set.Pass(), source, callback, |
| + error_callback); |
| + } |
| } |
| void NetworkConfigurationHandler::ClearProperties( |
| @@ -271,17 +312,12 @@ void NetworkConfigurationHandler::ClearProperties( |
| return; |
| } |
| NET_LOG_USER("ClearProperties", service_path); |
| - for (std::vector<std::string>::const_iterator iter = names.begin(); |
| - iter != names.end(); ++iter) { |
| - NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter); |
| - } |
| - DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties( |
| + // Shill triggers an error if we clear properties that are unset, so |
| + // first get the current properties. |
| + DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( |
| dbus::ObjectPath(service_path), |
| - names, |
| - base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback, |
| - AsWeakPtr(), service_path, names, callback), |
| - base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback, |
| - AsWeakPtr(), service_path, error_callback)); |
| + base::Bind(&NetworkConfigurationHandler::ClearPropertiesGetCallback, |
| + AsWeakPtr(), service_path, names, callback, error_callback)); |
| } |
| void NetworkConfigurationHandler::CreateConfiguration( |
| @@ -297,8 +333,7 @@ void NetworkConfigurationHandler::CreateConfiguration( |
| if (NetworkTypePattern::Ethernet().MatchesType(type)) { |
| InvokeErrorCallback( |
| shill_property_util::GetNetworkIdFromProperties(properties), |
| - error_callback, |
| - "ConfigureServiceForProfile: Invalid type: " + type); |
| + error_callback, "ConfigureServiceForProfile: Invalid type: " + type); |
| return; |
| } |
| @@ -328,8 +363,8 @@ void NetworkConfigurationHandler::RemoveConfiguration( |
| // Service.Remove is not reliable. Instead, request the profile entries |
| // for the service and remove each entry. |
| if (ContainsKey(profile_entry_deleters_, service_path)) { |
| - InvokeErrorCallback( |
| - service_path, error_callback, "RemoveConfigurationInProgress"); |
| + InvokeErrorCallback(service_path, error_callback, |
| + "RemoveConfigurationInProgress"); |
| return; |
| } |
| @@ -369,13 +404,15 @@ NetworkConfigurationHandler::NetworkConfigurationHandler() |
| } |
| NetworkConfigurationHandler::~NetworkConfigurationHandler() { |
| - STLDeleteContainerPairSecondPointers( |
| - profile_entry_deleters_.begin(), profile_entry_deleters_.end()); |
| + STLDeleteContainerPairSecondPointers(profile_entry_deleters_.begin(), |
| + profile_entry_deleters_.end()); |
| } |
| void NetworkConfigurationHandler::Init( |
| - NetworkStateHandler* network_state_handler) { |
| + NetworkStateHandler* network_state_handler, |
| + NetworkDeviceHandler* network_device_handler) { |
| network_state_handler_ = network_state_handler; |
| + network_device_handler_ = network_device_handler; |
| } |
| void NetworkConfigurationHandler::RunCreateNetworkCallback( |
| @@ -424,9 +461,44 @@ void NetworkConfigurationHandler::SetNetworkProfileCompleted( |
| OnConfigurationProfileChanged(service_path, profile_path, source)); |
| } |
| +void NetworkConfigurationHandler::SendSetPropertiesToShill( |
| + const std::string& service_path, |
| + scoped_ptr<base::DictionaryValue> changed_properties, |
| + scoped_ptr<base::DictionaryValue> properties_to_set, |
| + NetworkConfigurationObserver::Source source, |
| + const base::Closure& callback, |
| + const network_handler::ErrorCallback& error_callback) { |
| + if (properties_to_set->empty()) { |
| + SetPropertiesSuccessCallback(service_path, changed_properties.Pass(), |
| + source, callback); |
| + return; |
| + } |
| + |
| + LogConfigProperties("SetProperty", service_path, *properties_to_set); |
| + DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( |
| + dbus::ObjectPath(service_path), *properties_to_set, |
| + base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback, |
| + AsWeakPtr(), service_path, base::Passed(&changed_properties), |
| + source, callback), |
| + base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback, |
| + AsWeakPtr(), service_path, error_callback)); |
| + |
| + // If we set any properties that might affect IP config, request an IP config |
| + // refresh after calling SetProperties. Note: it is possible (but unlikely and |
| + // harmless) for both Set and Clear to trigger an IP Config refresh. |
| + for (base::DictionaryValue::Iterator iter(*properties_to_set); |
| + !iter.IsAtEnd(); iter.Advance()) { |
| + const std::string& key = iter.key(); |
| + if (PropertyRequiresIPConfigRefresh(key)) { |
| + RequestRefreshIPConfigs(service_path); |
| + break; |
| + } |
| + } |
| +} |
| + |
| void NetworkConfigurationHandler::SetPropertiesSuccessCallback( |
| const std::string& service_path, |
| - scoped_ptr<base::DictionaryValue> set_properties, |
| + scoped_ptr<base::DictionaryValue> changed_properties, |
| NetworkConfigurationObserver::Source source, |
| const base::Closure& callback) { |
| if (!callback.is_null()) |
| @@ -438,7 +510,7 @@ void NetworkConfigurationHandler::SetPropertiesSuccessCallback( |
| FOR_EACH_OBSERVER(NetworkConfigurationObserver, observers_, |
| OnPropertiesSet(service_path, network_state->guid(), |
| - *set_properties, source)); |
| + *changed_properties, source)); |
| network_state_handler_->RequestUpdateForNetwork(service_path); |
| } |
| @@ -448,21 +520,62 @@ void NetworkConfigurationHandler::SetPropertiesErrorCallback( |
| const std::string& dbus_error_name, |
| const std::string& dbus_error_message) { |
| network_handler::ShillErrorCallbackFunction( |
| - "Config.SetProperties Failed", |
| - service_path, error_callback, |
| + "Config.SetProperties Failed", service_path, error_callback, |
| dbus_error_name, dbus_error_message); |
| // Some properties may have changed so request an update regardless. |
| network_state_handler_->RequestUpdateForNetwork(service_path); |
| } |
| +void NetworkConfigurationHandler::ClearPropertiesGetCallback( |
| + const std::string& service_path, |
| + const std::vector<std::string>& names, |
| + const base::Closure& callback, |
| + const network_handler::ErrorCallback& error_callback, |
| + DBusMethodCallStatus call_status, |
| + const base::DictionaryValue& properties) { |
| + if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| + network_handler::RunErrorCallback(error_callback, service_path, |
| + network_handler::kDBusFailedError, |
| + network_handler::kDBusFailedErrorMessage); |
| + return; |
| + } |
| + |
| + // Clear only properties that are currently set. Also trigger an IP Configs |
| + // refresh if necessary (see note in SendSetPropertiesToShill). |
| + std::vector<std::string> properties_to_clear; |
| + bool refresh_ip_configs = false; |
| + for (const auto& key : names) { |
| + if (properties.HasKey(key)) { |
| + NET_LOG_DEBUG("ClearProperty", service_path + "." + key); |
| + properties_to_clear.push_back(key); |
| + if (PropertyRequiresIPConfigRefresh(key)) |
| + refresh_ip_configs = true; |
| + } else { |
| + NET_LOG_DEBUG("ClearProperty:Unset (Ignoring)", service_path + "." + key); |
| + } |
| + } |
| + |
| + DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties( |
| + dbus::ObjectPath(service_path), properties_to_clear, |
| + base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback, |
| + AsWeakPtr(), service_path, properties_to_clear, callback), |
| + base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback, |
| + AsWeakPtr(), service_path, error_callback)); |
| + |
| + // If we cleared any properties that might affect IP config, request an IP |
| + // config refresh after calling ClearProperties. |
| + if (refresh_ip_configs) |
| + RequestRefreshIPConfigs(service_path); |
| +} |
| + |
| void NetworkConfigurationHandler::ClearPropertiesSuccessCallback( |
| const std::string& service_path, |
| const std::vector<std::string>& names, |
| const base::Closure& callback, |
| const base::ListValue& result) { |
| const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed"); |
| - DCHECK(names.size() == result.GetSize()) |
| - << "Incorrect result size from ClearProperties."; |
| + if (names.size() != result.GetSize()) |
| + NET_LOG_ERROR("ClearProperties Result size != input", service_path); |
| for (size_t i = 0; i < result.GetSize(); ++i) { |
| bool success = false; |
| @@ -485,18 +598,31 @@ void NetworkConfigurationHandler::ClearPropertiesErrorCallback( |
| const std::string& dbus_error_name, |
| const std::string& dbus_error_message) { |
| network_handler::ShillErrorCallbackFunction( |
| - "Config.ClearProperties Failed", |
| - service_path, error_callback, |
| + "Config.ClearProperties Failed", service_path, error_callback, |
| dbus_error_name, dbus_error_message); |
| // Some properties may have changed so request an update regardless. |
| network_state_handler_->RequestUpdateForNetwork(service_path); |
| } |
| +void NetworkConfigurationHandler::RequestRefreshIPConfigs( |
| + const std::string& service_path) { |
| + if (!network_device_handler_) |
| + return; |
| + const NetworkState* network_state = |
| + network_state_handler_->GetNetworkState(service_path); |
| + if (!network_state || network_state->device_path().empty()) |
| + return; |
| + network_device_handler_->RequestRefreshIPConfigs( |
| + network_state->device_path(), base::Bind(&base::DoNothing), |
| + network_handler::ErrorCallback()); |
| +} |
| + |
| // static |
| NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest( |
| - NetworkStateHandler* network_state_handler) { |
| + NetworkStateHandler* network_state_handler, |
| + NetworkDeviceHandler* network_device_handler) { |
| NetworkConfigurationHandler* handler = new NetworkConfigurationHandler(); |
| - handler->Init(network_state_handler); |
| + handler->Init(network_state_handler, network_device_handler); |
| return handler; |
| } |