Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chromeos / network / network_configuration_handler.cc
blobac7ea8bcd1eaa1f90fcd6abd598f34ab9c6e8b2e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/network/network_configuration_handler.h"
8 #include "base/bind.h"
9 #include "base/format_macros.h"
10 #include "base/guid.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/stl_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/values.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/shill_manager_client.h"
19 #include "chromeos/dbus/shill_profile_client.h"
20 #include "chromeos/dbus/shill_service_client.h"
21 #include "chromeos/network/network_device_handler.h"
22 #include "chromeos/network/network_state.h"
23 #include "chromeos/network/network_state_handler.h"
24 #include "chromeos/network/shill_property_util.h"
25 #include "components/device_event_log/device_event_log.h"
26 #include "dbus/object_path.h"
27 #include "third_party/cros_system_api/dbus/service_constants.h"
29 namespace chromeos {
31 namespace {
33 // Strip surrounding "" from keys (if present).
34 std::string StripQuotations(const std::string& in_str) {
35 size_t len = in_str.length();
36 if (len >= 2 && in_str[0] == '"' && in_str[len - 1] == '"')
37 return in_str.substr(1, len - 2);
38 return in_str;
41 void InvokeErrorCallback(const std::string& service_path,
42 const network_handler::ErrorCallback& error_callback,
43 const std::string& error_name) {
44 std::string error_msg = "Config Error: " + error_name;
45 NET_LOG(ERROR) << error_msg << ": " << service_path;
46 network_handler::RunErrorCallback(error_callback, service_path, error_name,
47 error_msg);
50 void SetNetworkProfileErrorCallback(
51 const std::string& service_path,
52 const std::string& profile_path,
53 const network_handler::ErrorCallback& error_callback,
54 const std::string& dbus_error_name,
55 const std::string& dbus_error_message) {
56 network_handler::ShillErrorCallbackFunction(
57 "Config.SetNetworkProfile Failed: " + profile_path, service_path,
58 error_callback, dbus_error_name, dbus_error_message);
61 void LogConfigProperties(const std::string& desc,
62 const std::string& path,
63 const base::DictionaryValue& properties) {
64 for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
65 iter.Advance()) {
66 std::string v = "******";
67 if (shill_property_util::IsLoggableShillProperty(iter.key()))
68 base::JSONWriter::Write(iter.value(), &v);
69 NET_LOG(USER) << desc << ": " << path + "." + iter.key() + "=" + v;
73 } // namespace
75 // Helper class to request from Shill the profile entries associated with a
76 // Service and delete the service from each profile. Triggers either
77 // |callback| on success or |error_callback| on failure, and calls
78 // |handler|->ProfileEntryDeleterCompleted() on completion to delete itself.
79 class NetworkConfigurationHandler::ProfileEntryDeleter
80 : public base::SupportsWeakPtr<ProfileEntryDeleter> {
81 public:
82 ProfileEntryDeleter(NetworkConfigurationHandler* handler,
83 const std::string& service_path,
84 const std::string& guid,
85 NetworkConfigurationObserver::Source source,
86 const base::Closure& callback,
87 const network_handler::ErrorCallback& error_callback)
88 : owner_(handler),
89 service_path_(service_path),
90 guid_(guid),
91 source_(source),
92 callback_(callback),
93 error_callback_(error_callback) {}
95 void Run() {
96 DBusThreadManager::Get()
97 ->GetShillServiceClient()
98 ->GetLoadableProfileEntries(
99 dbus::ObjectPath(service_path_),
100 base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback,
101 AsWeakPtr()));
104 private:
105 void GetProfileEntriesToDeleteCallback(
106 DBusMethodCallStatus call_status,
107 const base::DictionaryValue& profile_entries) {
108 if (call_status != DBUS_METHOD_CALL_SUCCESS) {
109 InvokeErrorCallback(service_path_, error_callback_,
110 "GetLoadableProfileEntriesFailed");
111 // ProfileEntryDeleterCompleted will delete this.
112 owner_->ProfileEntryDeleterCompleted(service_path_, guid_, source_,
113 false /* failed */);
114 return;
117 for (base::DictionaryValue::Iterator iter(profile_entries); !iter.IsAtEnd();
118 iter.Advance()) {
119 std::string profile_path = StripQuotations(iter.key());
120 std::string entry_path;
121 iter.value().GetAsString(&entry_path);
122 if (profile_path.empty() || entry_path.empty()) {
123 NET_LOG(ERROR) << "Failed to parse Profile Entry: " << profile_path
124 << ": " << entry_path;
125 continue;
127 if (profile_delete_entries_.count(profile_path) != 0) {
128 NET_LOG(ERROR) << "Multiple Profile Entries: " << profile_path << ": "
129 << entry_path;
130 continue;
132 NET_LOG(DEBUG) << "Delete Profile Entry: " << profile_path << ": "
133 << entry_path;
134 profile_delete_entries_[profile_path] = entry_path;
135 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
136 dbus::ObjectPath(profile_path), entry_path,
137 base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback,
138 AsWeakPtr(), profile_path, entry_path),
139 base::Bind(&ProfileEntryDeleter::ShillErrorCallback, AsWeakPtr(),
140 profile_path, entry_path));
144 void ProfileEntryDeletedCallback(const std::string& profile_path,
145 const std::string& entry) {
146 NET_LOG(DEBUG) << "Profile Entry Deleted: " << profile_path << ": "
147 << entry;
148 profile_delete_entries_.erase(profile_path);
149 if (!profile_delete_entries_.empty())
150 return;
151 // Run the callback if this is the last pending deletion.
152 if (!callback_.is_null())
153 callback_.Run();
154 // ProfileEntryDeleterCompleted will delete this.
155 owner_->ProfileEntryDeleterCompleted(service_path_, guid_, source_,
156 true /* success */);
159 void ShillErrorCallback(const std::string& profile_path,
160 const std::string& entry,
161 const std::string& dbus_error_name,
162 const std::string& dbus_error_message) {
163 // Any Shill Error triggers a failure / error.
164 network_handler::ShillErrorCallbackFunction(
165 "GetLoadableProfileEntries Failed", profile_path, error_callback_,
166 dbus_error_name, dbus_error_message);
167 // Delete this even if there are pending deletions; any callbacks will
168 // safely become no-ops (by invalidating the WeakPtrs).
169 owner_->ProfileEntryDeleterCompleted(service_path_, guid_, source_,
170 false /* failed */);
173 NetworkConfigurationHandler* owner_; // Unowned
174 std::string service_path_;
175 std::string guid_;
176 NetworkConfigurationObserver::Source source_;
177 base::Closure callback_;
178 network_handler::ErrorCallback error_callback_;
180 // Map of pending profile entry deletions, indexed by profile path.
181 std::map<std::string, std::string> profile_delete_entries_;
183 DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter);
186 // NetworkConfigurationHandler
188 void NetworkConfigurationHandler::AddObserver(
189 NetworkConfigurationObserver* observer) {
190 observers_.AddObserver(observer);
193 void NetworkConfigurationHandler::RemoveObserver(
194 NetworkConfigurationObserver* observer) {
195 observers_.RemoveObserver(observer);
198 void NetworkConfigurationHandler::GetShillProperties(
199 const std::string& service_path,
200 const network_handler::DictionaryResultCallback& callback,
201 const network_handler::ErrorCallback& error_callback) {
202 NET_LOG(USER) << "GetShillProperties: " << service_path;
203 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
204 dbus::ObjectPath(service_path),
205 base::Bind(&NetworkConfigurationHandler::GetPropertiesCallback,
206 AsWeakPtr(), callback, error_callback, service_path));
209 void NetworkConfigurationHandler::SetShillProperties(
210 const std::string& service_path,
211 const base::DictionaryValue& shill_properties,
212 NetworkConfigurationObserver::Source source,
213 const base::Closure& callback,
214 const network_handler::ErrorCallback& error_callback) {
215 if (shill_properties.empty()) {
216 if (!callback.is_null())
217 callback.Run();
218 return;
220 NET_LOG(USER) << "SetShillProperties: " << service_path;
222 scoped_ptr<base::DictionaryValue> properties_to_set(
223 shill_properties.DeepCopy());
225 // Make sure that the GUID is saved to Shill when setting properties.
226 std::string guid;
227 properties_to_set->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
228 if (guid.empty()) {
229 const NetworkState* network_state =
230 network_state_handler_->GetNetworkState(service_path);
231 guid = network_state ? network_state->guid() : base::GenerateGUID();
232 properties_to_set->SetStringWithoutPathExpansion(shill::kGuidProperty,
233 guid);
236 LogConfigProperties("SetProperty", service_path, *properties_to_set);
238 scoped_ptr<base::DictionaryValue> properties_copy(
239 properties_to_set->DeepCopy());
240 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
241 dbus::ObjectPath(service_path), *properties_to_set,
242 base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback,
243 AsWeakPtr(), service_path, base::Passed(&properties_copy),
244 source, callback),
245 base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback,
246 AsWeakPtr(), service_path, error_callback));
248 // If we set the StaticIPConfig property, request an IP config refresh
249 // after calling SetProperties.
250 if (properties_to_set->HasKey(shill::kStaticIPConfigProperty))
251 RequestRefreshIPConfigs(service_path);
254 void NetworkConfigurationHandler::ClearShillProperties(
255 const std::string& service_path,
256 const std::vector<std::string>& names,
257 const base::Closure& callback,
258 const network_handler::ErrorCallback& error_callback) {
259 if (names.empty()) {
260 if (!callback.is_null())
261 callback.Run();
262 return;
264 NET_LOG(USER) << "ClearShillProperties: " << service_path;
265 for (std::vector<std::string>::const_iterator iter = names.begin();
266 iter != names.end(); ++iter) {
267 NET_LOG(DEBUG) << "ClearProperty: " << service_path << "." << *iter;
269 DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties(
270 dbus::ObjectPath(service_path), names,
271 base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback,
272 AsWeakPtr(), service_path, names, callback),
273 base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback,
274 AsWeakPtr(), service_path, error_callback));
277 void NetworkConfigurationHandler::CreateShillConfiguration(
278 const base::DictionaryValue& shill_properties,
279 NetworkConfigurationObserver::Source source,
280 const network_handler::StringResultCallback& callback,
281 const network_handler::ErrorCallback& error_callback) {
282 ShillManagerClient* manager =
283 DBusThreadManager::Get()->GetShillManagerClient();
284 std::string type;
285 shill_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
286 DCHECK(!type.empty());
288 std::string network_id =
289 shill_property_util::GetNetworkIdFromProperties(shill_properties);
291 if (NetworkTypePattern::Ethernet().MatchesType(type)) {
292 InvokeErrorCallback(network_id, error_callback,
293 "ConfigureServiceForProfile: Invalid type: " + type);
294 return;
297 scoped_ptr<base::DictionaryValue> properties_to_set(
298 shill_properties.DeepCopy());
300 NET_LOG(USER) << "CreateShillConfiguration: " << type << ": " << network_id;
302 std::string profile_path;
303 properties_to_set->GetStringWithoutPathExpansion(shill::kProfileProperty,
304 &profile_path);
305 DCHECK(!profile_path.empty());
307 // Make sure that the GUID is saved to Shill when configuring networks.
308 std::string guid;
309 properties_to_set->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
310 if (guid.empty()) {
311 guid = base::GenerateGUID();
312 properties_to_set->SetStringWithoutPathExpansion(
313 ::onc::network_config::kGUID, guid);
316 LogConfigProperties("Configure", type, *properties_to_set);
318 scoped_ptr<base::DictionaryValue> properties_copy(
319 properties_to_set->DeepCopy());
320 manager->ConfigureServiceForProfile(
321 dbus::ObjectPath(profile_path), *properties_to_set,
322 base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback,
323 AsWeakPtr(), profile_path, source,
324 base::Passed(&properties_copy), callback),
325 base::Bind(&network_handler::ShillErrorCallbackFunction,
326 "Config.CreateConfiguration Failed", "", error_callback));
329 void NetworkConfigurationHandler::RemoveConfiguration(
330 const std::string& service_path,
331 NetworkConfigurationObserver::Source source,
332 const base::Closure& callback,
333 const network_handler::ErrorCallback& error_callback) {
334 // Service.Remove is not reliable. Instead, request the profile entries
335 // for the service and remove each entry.
336 if (ContainsKey(profile_entry_deleters_, service_path)) {
337 InvokeErrorCallback(service_path, error_callback,
338 "RemoveConfigurationInProgress");
339 return;
342 std::string guid;
343 const NetworkState* network_state =
344 network_state_handler_->GetNetworkState(service_path);
345 if (network_state)
346 guid = network_state->guid();
347 NET_LOG(USER) << "Remove Configuration: " << service_path;
348 ProfileEntryDeleter* deleter = new ProfileEntryDeleter(
349 this, service_path, guid, source, callback, error_callback);
350 profile_entry_deleters_[service_path] = deleter;
351 deleter->Run();
354 void NetworkConfigurationHandler::SetNetworkProfile(
355 const std::string& service_path,
356 const std::string& profile_path,
357 NetworkConfigurationObserver::Source source,
358 const base::Closure& callback,
359 const network_handler::ErrorCallback& error_callback) {
360 NET_LOG(USER) << "SetNetworkProfile: " << service_path << ": "
361 << profile_path;
362 base::StringValue profile_path_value(profile_path);
363 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
364 dbus::ObjectPath(service_path), shill::kProfileProperty,
365 profile_path_value,
366 base::Bind(&NetworkConfigurationHandler::SetNetworkProfileCompleted,
367 AsWeakPtr(), service_path, profile_path, source, callback),
368 base::Bind(&SetNetworkProfileErrorCallback, service_path, profile_path,
369 error_callback));
372 // NetworkConfigurationHandler Private methods
374 NetworkConfigurationHandler::NetworkConfigurationHandler()
375 : network_state_handler_(NULL) {
378 NetworkConfigurationHandler::~NetworkConfigurationHandler() {
379 STLDeleteContainerPairSecondPointers(profile_entry_deleters_.begin(),
380 profile_entry_deleters_.end());
383 void NetworkConfigurationHandler::Init(
384 NetworkStateHandler* network_state_handler,
385 NetworkDeviceHandler* network_device_handler) {
386 network_state_handler_ = network_state_handler;
387 network_device_handler_ = network_device_handler;
390 void NetworkConfigurationHandler::RunCreateNetworkCallback(
391 const std::string& profile_path,
392 NetworkConfigurationObserver::Source source,
393 scoped_ptr<base::DictionaryValue> configure_properties,
394 const network_handler::StringResultCallback& callback,
395 const dbus::ObjectPath& service_path) {
396 if (!callback.is_null())
397 callback.Run(service_path.value());
398 FOR_EACH_OBSERVER(NetworkConfigurationObserver, observers_,
399 OnConfigurationCreated(service_path.value(), profile_path,
400 *configure_properties, source));
401 // This may also get called when CreateConfiguration is used to update an
402 // existing configuration, so request a service update just in case.
403 // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger
404 // this on an update.
405 network_state_handler_->RequestUpdateForNetwork(service_path.value());
408 void NetworkConfigurationHandler::ProfileEntryDeleterCompleted(
409 const std::string& service_path,
410 const std::string& guid,
411 NetworkConfigurationObserver::Source source,
412 bool success) {
413 if (success) {
414 FOR_EACH_OBSERVER(NetworkConfigurationObserver, observers_,
415 OnConfigurationRemoved(service_path, guid, source));
417 std::map<std::string, ProfileEntryDeleter*>::iterator iter =
418 profile_entry_deleters_.find(service_path);
419 DCHECK(iter != profile_entry_deleters_.end());
420 delete iter->second;
421 profile_entry_deleters_.erase(iter);
424 void NetworkConfigurationHandler::SetNetworkProfileCompleted(
425 const std::string& service_path,
426 const std::string& profile_path,
427 NetworkConfigurationObserver::Source source,
428 const base::Closure& callback) {
429 if (!callback.is_null())
430 callback.Run();
431 FOR_EACH_OBSERVER(
432 NetworkConfigurationObserver, observers_,
433 OnConfigurationProfileChanged(service_path, profile_path, source));
436 void NetworkConfigurationHandler::GetPropertiesCallback(
437 const network_handler::DictionaryResultCallback& callback,
438 const network_handler::ErrorCallback& error_callback,
439 const std::string& service_path,
440 DBusMethodCallStatus call_status,
441 const base::DictionaryValue& properties) {
442 if (call_status != DBUS_METHOD_CALL_SUCCESS) {
443 // Because network services are added and removed frequently, we will see
444 // failures regularly, so don't log these.
445 network_handler::RunErrorCallback(error_callback, service_path,
446 network_handler::kDBusFailedError,
447 network_handler::kDBusFailedErrorMessage);
448 return;
450 if (callback.is_null())
451 return;
453 // Get the correct name from WifiHex if necessary.
454 scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
455 std::string name =
456 shill_property_util::GetNameFromProperties(service_path, properties);
457 if (!name.empty())
458 properties_copy->SetStringWithoutPathExpansion(shill::kNameProperty, name);
460 // Get the GUID property from NetworkState if it is not set in Shill.
461 std::string guid;
462 properties.GetStringWithoutPathExpansion(::onc::network_config::kGUID, &guid);
463 if (guid.empty()) {
464 const NetworkState* network_state =
465 network_state_handler_->GetNetworkState(service_path);
466 if (network_state) {
467 properties_copy->SetStringWithoutPathExpansion(
468 ::onc::network_config::kGUID, network_state->guid());
472 callback.Run(service_path, *properties_copy.get());
475 void NetworkConfigurationHandler::SetPropertiesSuccessCallback(
476 const std::string& service_path,
477 scoped_ptr<base::DictionaryValue> set_properties,
478 NetworkConfigurationObserver::Source source,
479 const base::Closure& callback) {
480 if (!callback.is_null())
481 callback.Run();
482 const NetworkState* network_state =
483 network_state_handler_->GetNetworkState(service_path);
484 if (!network_state)
485 return; // Network no longer exists, do not notify or request update.
487 FOR_EACH_OBSERVER(NetworkConfigurationObserver, observers_,
488 OnPropertiesSet(service_path, network_state->guid(),
489 *set_properties, source));
490 network_state_handler_->RequestUpdateForNetwork(service_path);
493 void NetworkConfigurationHandler::SetPropertiesErrorCallback(
494 const std::string& service_path,
495 const network_handler::ErrorCallback& error_callback,
496 const std::string& dbus_error_name,
497 const std::string& dbus_error_message) {
498 network_handler::ShillErrorCallbackFunction(
499 "Config.SetProperties Failed", service_path, error_callback,
500 dbus_error_name, dbus_error_message);
501 // Some properties may have changed so request an update regardless.
502 network_state_handler_->RequestUpdateForNetwork(service_path);
505 void NetworkConfigurationHandler::ClearPropertiesSuccessCallback(
506 const std::string& service_path,
507 const std::vector<std::string>& names,
508 const base::Closure& callback,
509 const base::ListValue& result) {
510 const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed");
511 DCHECK(names.size() == result.GetSize())
512 << "Incorrect result size from ClearProperties.";
514 for (size_t i = 0; i < result.GetSize(); ++i) {
515 bool success = false;
516 result.GetBoolean(i, &success);
517 if (!success) {
518 // If a property was cleared that has never been set, the clear will fail.
519 // We do not track which properties have been set, so just log the error.
520 NET_LOG(ERROR) << "ClearProperties Failed: " << service_path << ": "
521 << names[i];
525 if (!callback.is_null())
526 callback.Run();
527 network_state_handler_->RequestUpdateForNetwork(service_path);
530 void NetworkConfigurationHandler::ClearPropertiesErrorCallback(
531 const std::string& service_path,
532 const network_handler::ErrorCallback& error_callback,
533 const std::string& dbus_error_name,
534 const std::string& dbus_error_message) {
535 network_handler::ShillErrorCallbackFunction(
536 "Config.ClearProperties Failed", service_path, error_callback,
537 dbus_error_name, dbus_error_message);
538 // Some properties may have changed so request an update regardless.
539 network_state_handler_->RequestUpdateForNetwork(service_path);
542 void NetworkConfigurationHandler::RequestRefreshIPConfigs(
543 const std::string& service_path) {
544 if (!network_device_handler_)
545 return;
546 const NetworkState* network_state =
547 network_state_handler_->GetNetworkState(service_path);
548 if (!network_state || network_state->device_path().empty())
549 return;
550 network_device_handler_->RequestRefreshIPConfigs(
551 network_state->device_path(), base::Bind(&base::DoNothing),
552 network_handler::ErrorCallback());
555 // static
556 NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest(
557 NetworkStateHandler* network_state_handler,
558 NetworkDeviceHandler* network_device_handler) {
559 NetworkConfigurationHandler* handler = new NetworkConfigurationHandler();
560 handler->Init(network_state_handler, network_device_handler);
561 return handler;
564 } // namespace chromeos