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"
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"
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);
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
,
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();
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
;
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
> {
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
)
89 service_path_(service_path
),
93 error_callback_(error_callback
) {}
96 DBusThreadManager::Get()
97 ->GetShillServiceClient()
98 ->GetLoadableProfileEntries(
99 dbus::ObjectPath(service_path_
),
100 base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback
,
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_
,
117 for (base::DictionaryValue::Iterator
iter(profile_entries
); !iter
.IsAtEnd();
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
;
127 if (profile_delete_entries_
.count(profile_path
) != 0) {
128 NET_LOG(ERROR
) << "Multiple Profile Entries: " << profile_path
<< ": "
132 NET_LOG(DEBUG
) << "Delete Profile Entry: " << profile_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
<< ": "
148 profile_delete_entries_
.erase(profile_path
);
149 if (!profile_delete_entries_
.empty())
151 // Run the callback if this is the last pending deletion.
152 if (!callback_
.is_null())
154 // ProfileEntryDeleterCompleted will delete this.
155 owner_
->ProfileEntryDeleterCompleted(service_path_
, guid_
, source_
,
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_
,
173 NetworkConfigurationHandler
* owner_
; // Unowned
174 std::string service_path_
;
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())
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.
227 properties_to_set
->GetStringWithoutPathExpansion(shill::kGuidProperty
, &guid
);
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
,
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
),
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
) {
260 if (!callback
.is_null())
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();
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
);
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
,
305 DCHECK(!profile_path
.empty());
307 // Make sure that the GUID is saved to Shill when configuring networks.
309 properties_to_set
->GetStringWithoutPathExpansion(shill::kGuidProperty
, &guid
);
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");
343 const NetworkState
* network_state
=
344 network_state_handler_
->GetNetworkState(service_path
);
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
;
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
<< ": "
362 base::StringValue
profile_path_value(profile_path
);
363 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
364 dbus::ObjectPath(service_path
), shill::kProfileProperty
,
366 base::Bind(&NetworkConfigurationHandler::SetNetworkProfileCompleted
,
367 AsWeakPtr(), service_path
, profile_path
, source
, callback
),
368 base::Bind(&SetNetworkProfileErrorCallback
, service_path
, profile_path
,
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
,
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());
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())
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
);
450 if (callback
.is_null())
453 // Get the correct name from WifiHex if necessary.
454 scoped_ptr
<base::DictionaryValue
> properties_copy(properties
.DeepCopy());
456 shill_property_util::GetNameFromProperties(service_path
, properties
);
458 properties_copy
->SetStringWithoutPathExpansion(shill::kNameProperty
, name
);
460 // Get the GUID property from NetworkState if it is not set in Shill.
462 properties
.GetStringWithoutPathExpansion(::onc::network_config::kGUID
, &guid
);
464 const NetworkState
* network_state
=
465 network_state_handler_
->GetNetworkState(service_path
);
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())
482 const NetworkState
* network_state
=
483 network_state_handler_
->GetNetworkState(service_path
);
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
);
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
<< ": "
525 if (!callback
.is_null())
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_
)
546 const NetworkState
* network_state
=
547 network_state_handler_
->GetNetworkState(service_path
);
548 if (!network_state
|| network_state
->device_path().empty())
550 network_device_handler_
->RequestRefreshIPConfigs(
551 network_state
->device_path(), base::Bind(&base::DoNothing
),
552 network_handler::ErrorCallback());
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
);
564 } // namespace chromeos