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/shill_property_handler.h"
8 #include "base/format_macros.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/shill_device_client.h"
15 #include "chromeos/dbus/shill_ipconfig_client.h"
16 #include "chromeos/dbus/shill_manager_client.h"
17 #include "chromeos/dbus/shill_profile_client.h"
18 #include "chromeos/dbus/shill_service_client.h"
19 #include "chromeos/network/network_event_log.h"
20 #include "chromeos/network/network_state.h"
21 #include "dbus/object_path.h"
22 #include "third_party/cros_system_api/dbus/service_constants.h"
26 // Limit the number of services or devices we observe. Since they are listed in
27 // priority order, it should be reasonable to ignore services past this.
28 const size_t kMaxObserved
= 100;
30 const base::ListValue
* GetListValue(const std::string
& key
,
31 const base::Value
& value
) {
32 const base::ListValue
* vlist
= NULL
;
33 if (!value
.GetAsList(&vlist
)) {
34 LOG(ERROR
) << "Error parsing key as list: " << key
;
45 // Class to manage Shill service property changed observers. Observers are
46 // added on construction and removed on destruction. Runs the handler when
47 // OnPropertyChanged is called.
48 class ShillPropertyObserver
: public ShillPropertyChangedObserver
{
50 typedef base::Callback
<void(ManagedState::ManagedType type
,
51 const std::string
& service
,
52 const std::string
& name
,
53 const base::Value
& value
)> Handler
;
55 ShillPropertyObserver(ManagedState::ManagedType type
,
56 const std::string
& path
,
57 const Handler
& handler
)
61 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
62 DBusThreadManager::Get()->GetShillServiceClient()->
63 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
64 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
65 DBusThreadManager::Get()->GetShillDeviceClient()->
66 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
72 virtual ~ShillPropertyObserver() {
73 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
74 DBusThreadManager::Get()->GetShillServiceClient()->
75 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
76 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
77 DBusThreadManager::Get()->GetShillDeviceClient()->
78 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
84 // ShillPropertyChangedObserver overrides.
85 virtual void OnPropertyChanged(const std::string
& key
,
86 const base::Value
& value
) OVERRIDE
{
87 handler_
.Run(type_
, path_
, key
, value
);
91 ManagedState::ManagedType type_
;
95 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver
);
98 //------------------------------------------------------------------------------
99 // ShillPropertyHandler
101 ShillPropertyHandler::ShillPropertyHandler(Listener
* listener
)
102 : listener_(listener
),
103 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
106 ShillPropertyHandler::~ShillPropertyHandler() {
107 // Delete network service observers.
108 STLDeleteContainerPairSecondPointers(
109 observed_networks_
.begin(), observed_networks_
.end());
110 STLDeleteContainerPairSecondPointers(
111 observed_devices_
.begin(), observed_devices_
.end());
112 CHECK(shill_manager_
== DBusThreadManager::Get()->GetShillManagerClient());
113 shill_manager_
->RemovePropertyChangedObserver(this);
116 void ShillPropertyHandler::Init() {
117 UpdateManagerProperties();
118 shill_manager_
->AddPropertyChangedObserver(this);
121 void ShillPropertyHandler::UpdateManagerProperties() {
122 NET_LOG_EVENT("UpdateManagerProperties", "");
123 shill_manager_
->GetProperties(
124 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback
,
128 bool ShillPropertyHandler::IsTechnologyAvailable(
129 const std::string
& technology
) const {
130 return available_technologies_
.count(technology
) != 0;
133 bool ShillPropertyHandler::IsTechnologyEnabled(
134 const std::string
& technology
) const {
135 return enabled_technologies_
.count(technology
) != 0;
138 bool ShillPropertyHandler::IsTechnologyEnabling(
139 const std::string
& technology
) const {
140 return enabling_technologies_
.count(technology
) != 0;
143 bool ShillPropertyHandler::IsTechnologyUninitialized(
144 const std::string
& technology
) const {
145 return uninitialized_technologies_
.count(technology
) != 0;
148 void ShillPropertyHandler::SetTechnologyEnabled(
149 const std::string
& technology
,
151 const network_handler::ErrorCallback
& error_callback
) {
153 enabling_technologies_
.insert(technology
);
154 shill_manager_
->EnableTechnology(
156 base::Bind(&base::DoNothing
),
157 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed
,
158 AsWeakPtr(), technology
, error_callback
));
160 // Immediately clear locally from enabled and enabling lists.
161 enabled_technologies_
.erase(technology
);
162 enabling_technologies_
.erase(technology
);
163 shill_manager_
->DisableTechnology(
165 base::Bind(&base::DoNothing
),
166 base::Bind(&network_handler::ShillErrorCallbackFunction
,
167 "SetTechnologyEnabled Failed",
168 technology
, error_callback
));
172 void ShillPropertyHandler::SetCheckPortalList(
173 const std::string
& check_portal_list
) {
174 base::StringValue
value(check_portal_list
);
175 shill_manager_
->SetProperty(
176 shill::kCheckPortalListProperty
,
178 base::Bind(&base::DoNothing
),
179 base::Bind(&network_handler::ShillErrorCallbackFunction
,
180 "SetCheckPortalList Failed",
181 "", network_handler::ErrorCallback()));
184 void ShillPropertyHandler::RequestScan() const {
185 shill_manager_
->RequestScan(
187 base::Bind(&base::DoNothing
),
188 base::Bind(&network_handler::ShillErrorCallbackFunction
,
189 "RequestScan Failed",
190 "", network_handler::ErrorCallback()));
193 void ShillPropertyHandler::ConnectToBestServices() const {
194 NET_LOG_EVENT("ConnectToBestServices", "");
195 shill_manager_
->ConnectToBestServices(
196 base::Bind(&base::DoNothing
),
197 base::Bind(&network_handler::ShillErrorCallbackFunction
,
198 "ConnectToBestServices Failed",
199 "", network_handler::ErrorCallback()));
202 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type
,
203 const std::string
& path
) {
204 if (pending_updates_
[type
].find(path
) != pending_updates_
[type
].end())
205 return; // Update already requested.
207 NET_LOG_DEBUG("Request Properties", path
);
208 pending_updates_
[type
].insert(path
);
209 if (type
== ManagedState::MANAGED_TYPE_NETWORK
||
210 type
== ManagedState::MANAGED_TYPE_FAVORITE
) {
211 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
212 dbus::ObjectPath(path
),
213 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
214 AsWeakPtr(), type
, path
));
215 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
216 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
217 dbus::ObjectPath(path
),
218 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
219 AsWeakPtr(), type
, path
));
225 void ShillPropertyHandler::OnPropertyChanged(const std::string
& key
,
226 const base::Value
& value
) {
227 ManagerPropertyChanged(key
, value
);
228 CheckPendingStateListUpdates(key
);
231 //------------------------------------------------------------------------------
234 void ShillPropertyHandler::ManagerPropertiesCallback(
235 DBusMethodCallStatus call_status
,
236 const base::DictionaryValue
& properties
) {
237 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
238 NET_LOG_ERROR("ManagerPropertiesCallback",
239 base::StringPrintf("Failed: %d", call_status
));
242 NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
243 const base::Value
* update_service_value
= NULL
;
244 const base::Value
* update_service_complete_value
= NULL
;
245 for (base::DictionaryValue::Iterator
iter(properties
);
246 !iter
.IsAtEnd(); iter
.Advance()) {
247 // Defer updating Services until all other properties have been updated.
248 if (iter
.key() == shill::kServicesProperty
)
249 update_service_value
= &iter
.value();
250 else if (iter
.key() == shill::kServiceCompleteListProperty
)
251 update_service_complete_value
= &iter
.value();
253 ManagerPropertyChanged(iter
.key(), iter
.value());
255 // Update Services which can safely assume other properties have been set.
256 if (update_service_value
)
257 ManagerPropertyChanged(shill::kServicesProperty
, *update_service_value
);
258 // Update ServiceCompleteList which skips entries that have already been
259 // requested for Services.
260 if (update_service_complete_value
) {
261 ManagerPropertyChanged(shill::kServiceCompleteListProperty
,
262 *update_service_complete_value
);
265 CheckPendingStateListUpdates("");
268 void ShillPropertyHandler::CheckPendingStateListUpdates(
269 const std::string
& key
) {
270 // Once there are no pending updates, signal the state list changed callbacks.
271 if ((key
.empty() || key
== shill::kServicesProperty
) &&
272 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0) {
273 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
275 // Both Network update requests and Favorite update requests will affect
276 // the list of favorites, so wait for both to complete.
277 if ((key
.empty() || key
== shill::kServiceCompleteListProperty
) &&
278 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0 &&
279 pending_updates_
[ManagedState::MANAGED_TYPE_FAVORITE
].size() == 0) {
280 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE
);
282 if ((key
.empty() || key
== shill::kDevicesProperty
) &&
283 pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0) {
284 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
288 void ShillPropertyHandler::ManagerPropertyChanged(const std::string
& key
,
289 const base::Value
& value
) {
290 if (key
== shill::kDefaultServiceProperty
) {
291 std::string service_path
;
292 value
.GetAsString(&service_path
);
293 listener_
->DefaultNetworkServiceChanged(service_path
);
294 } else if (key
== shill::kServicesProperty
) {
295 const base::ListValue
* vlist
= GetListValue(key
, value
);
297 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
298 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
299 // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK,
300 // however that prevents us from receiving Strength updates from inactive
301 // networks. The overhead for observing all services is not unreasonable
302 // (and we limit the max number of observed services to kMaxObserved).
303 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
305 } else if (key
== shill::kServiceCompleteListProperty
) {
306 const base::ListValue
* vlist
= GetListValue(key
, value
);
308 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_FAVORITE
, *vlist
);
309 UpdateProperties(ManagedState::MANAGED_TYPE_FAVORITE
, *vlist
);
311 } else if (key
== shill::kDevicesProperty
) {
312 const base::ListValue
* vlist
= GetListValue(key
, value
);
314 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
315 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
316 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
318 } else if (key
== shill::kAvailableTechnologiesProperty
) {
319 const base::ListValue
* vlist
= GetListValue(key
, value
);
321 UpdateAvailableTechnologies(*vlist
);
322 } else if (key
== shill::kEnabledTechnologiesProperty
) {
323 const base::ListValue
* vlist
= GetListValue(key
, value
);
325 UpdateEnabledTechnologies(*vlist
);
326 } else if (key
== shill::kUninitializedTechnologiesProperty
) {
327 const base::ListValue
* vlist
= GetListValue(key
, value
);
329 UpdateUninitializedTechnologies(*vlist
);
330 } else if (key
== shill::kProfilesProperty
) {
331 listener_
->ProfileListChanged();
332 } else if (key
== shill::kCheckPortalListProperty
) {
333 std::string check_portal_list
;
334 if (value
.GetAsString(&check_portal_list
))
335 listener_
->CheckPortalListChanged(check_portal_list
);
337 VLOG(2) << "Ignored Manager Property: " << key
;
341 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type
,
342 const base::ListValue
& entries
) {
343 std::set
<std::string
>& requested_updates
= requested_updates_
[type
];
344 std::set
<std::string
>& requested_service_updates
=
345 requested_updates_
[ManagedState::MANAGED_TYPE_NETWORK
]; // For favorites
346 std::set
<std::string
> new_requested_updates
;
348 base::StringPrintf("UpdateProperties: %" PRIuS
, entries
.GetSize()),
349 ManagedState::TypeToString(type
));
350 for (base::ListValue::const_iterator iter
= entries
.begin();
351 iter
!= entries
.end(); ++iter
) {
353 (*iter
)->GetAsString(&path
);
356 if (type
== ManagedState::MANAGED_TYPE_FAVORITE
&&
357 requested_service_updates
.count(path
) > 0)
358 continue; // Update already requested
360 // We add a special case for devices here to work around an issue in shill
361 // that prevents it from sending property changed signals for cellular
362 // devices (see crbug.com/321854).
363 if (type
== ManagedState::MANAGED_TYPE_DEVICE
||
364 requested_updates
.find(path
) == requested_updates
.end())
365 RequestProperties(type
, path
);
366 new_requested_updates
.insert(path
);
368 requested_updates
.swap(new_requested_updates
);
371 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type
,
372 const base::ListValue
& entries
) {
373 DCHECK(type
== ManagedState::MANAGED_TYPE_NETWORK
||
374 type
== ManagedState::MANAGED_TYPE_DEVICE
);
375 ShillPropertyObserverMap
& observer_map
=
376 (type
== ManagedState::MANAGED_TYPE_NETWORK
)
377 ? observed_networks_
: observed_devices_
;
378 ShillPropertyObserverMap new_observed
;
379 for (base::ListValue::const_iterator iter1
= entries
.begin();
380 iter1
!= entries
.end(); ++iter1
) {
382 (*iter1
)->GetAsString(&path
);
385 ShillPropertyObserverMap::iterator iter2
= observer_map
.find(path
);
386 if (iter2
!= observer_map
.end()) {
387 new_observed
[path
] = iter2
->second
;
389 // Create an observer for future updates.
390 new_observed
[path
] = new ShillPropertyObserver(
391 type
, path
, base::Bind(
392 &ShillPropertyHandler::PropertyChangedCallback
, AsWeakPtr()));
394 observer_map
.erase(path
);
395 // Limit the number of observed services.
396 if (new_observed
.size() >= kMaxObserved
)
399 // Delete network service observers still in observer_map.
400 for (ShillPropertyObserverMap::iterator iter
= observer_map
.begin();
401 iter
!= observer_map
.end(); ++iter
) {
404 observer_map
.swap(new_observed
);
407 void ShillPropertyHandler::UpdateAvailableTechnologies(
408 const base::ListValue
& technologies
) {
409 available_technologies_
.clear();
410 NET_LOG_EVENT("AvailableTechnologiesChanged",
411 base::StringPrintf("Size: %" PRIuS
, technologies
.GetSize()));
412 for (base::ListValue::const_iterator iter
= technologies
.begin();
413 iter
!= technologies
.end(); ++iter
) {
414 std::string technology
;
415 (*iter
)->GetAsString(&technology
);
416 DCHECK(!technology
.empty());
417 available_technologies_
.insert(technology
);
419 listener_
->TechnologyListChanged();
422 void ShillPropertyHandler::UpdateEnabledTechnologies(
423 const base::ListValue
& technologies
) {
424 enabled_technologies_
.clear();
425 NET_LOG_EVENT("EnabledTechnologiesChanged",
426 base::StringPrintf("Size: %" PRIuS
, technologies
.GetSize()));
427 for (base::ListValue::const_iterator iter
= technologies
.begin();
428 iter
!= technologies
.end(); ++iter
) {
429 std::string technology
;
430 (*iter
)->GetAsString(&technology
);
431 DCHECK(!technology
.empty());
432 enabled_technologies_
.insert(technology
);
433 enabling_technologies_
.erase(technology
);
435 listener_
->TechnologyListChanged();
438 void ShillPropertyHandler::UpdateUninitializedTechnologies(
439 const base::ListValue
& technologies
) {
440 uninitialized_technologies_
.clear();
441 NET_LOG_EVENT("UninitializedTechnologiesChanged",
442 base::StringPrintf("Size: %" PRIuS
, technologies
.GetSize()));
443 for (base::ListValue::const_iterator iter
= technologies
.begin();
444 iter
!= technologies
.end(); ++iter
) {
445 std::string technology
;
446 (*iter
)->GetAsString(&technology
);
447 DCHECK(!technology
.empty());
448 uninitialized_technologies_
.insert(technology
);
450 listener_
->TechnologyListChanged();
453 void ShillPropertyHandler::EnableTechnologyFailed(
454 const std::string
& technology
,
455 const network_handler::ErrorCallback
& error_callback
,
456 const std::string
& dbus_error_name
,
457 const std::string
& dbus_error_message
) {
458 enabling_technologies_
.erase(technology
);
459 network_handler::ShillErrorCallbackFunction(
460 "EnableTechnology Failed",
461 technology
, error_callback
,
462 dbus_error_name
, dbus_error_message
);
465 void ShillPropertyHandler::GetPropertiesCallback(
466 ManagedState::ManagedType type
,
467 const std::string
& path
,
468 DBusMethodCallStatus call_status
,
469 const base::DictionaryValue
& properties
) {
470 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type
),
472 pending_updates_
[type
].erase(path
);
473 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
474 // The shill service no longer exists. This can happen when a network
476 NET_LOG_DEBUG("Failed to get properties",
477 base::StringPrintf("%s: %d", path
.c_str(), call_status
));
480 // Update Favorite properties for networks in the Services list.
481 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
482 // Only networks with a ProfilePath set are Favorites.
483 std::string profile_path
;
484 properties
.GetStringWithoutPathExpansion(
485 shill::kProfileProperty
, &profile_path
);
486 if (!profile_path
.empty()) {
487 listener_
->UpdateManagedStateProperties(
488 ManagedState::MANAGED_TYPE_FAVORITE
, path
, properties
);
491 listener_
->UpdateManagedStateProperties(type
, path
, properties
);
492 // Request IPConfig parameters for networks.
493 if (type
== ManagedState::MANAGED_TYPE_NETWORK
&&
494 properties
.HasKey(shill::kIPConfigProperty
)) {
495 std::string ip_config_path
;
496 if (properties
.GetString(shill::kIPConfigProperty
, &ip_config_path
)) {
497 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
498 dbus::ObjectPath(ip_config_path
),
499 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
504 // Notify the listener only when all updates for that type have completed.
505 if (pending_updates_
[type
].size() == 0) {
506 listener_
->ManagedStateListChanged(type
);
507 // Notify that Favorites have changed when notifying for Networks if there
508 // are no additional Favorite updates pending.
509 if (type
== ManagedState::MANAGED_TYPE_NETWORK
&&
510 pending_updates_
[ManagedState::MANAGED_TYPE_FAVORITE
].size() == 0) {
511 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE
);
516 void ShillPropertyHandler::PropertyChangedCallback(
517 ManagedState::ManagedType type
,
518 const std::string
& path
,
519 const std::string
& key
,
520 const base::Value
& value
) {
521 if (type
== ManagedState::MANAGED_TYPE_NETWORK
)
522 NetworkServicePropertyChangedCallback(path
, key
, value
);
523 else if (type
== ManagedState::MANAGED_TYPE_DEVICE
)
524 NetworkDevicePropertyChangedCallback(path
, key
, value
);
529 void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
530 const std::string
& path
,
531 const std::string
& key
,
532 const base::Value
& value
) {
533 if (key
== shill::kIPConfigProperty
) {
534 // Request the IPConfig for the network and update network properties
535 // when the request completes.
536 std::string ip_config_path
;
537 value
.GetAsString(&ip_config_path
);
538 DCHECK(!ip_config_path
.empty());
539 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
540 dbus::ObjectPath(ip_config_path
),
541 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
544 listener_
->UpdateNetworkServiceProperty(path
, key
, value
);
548 void ShillPropertyHandler::GetIPConfigCallback(
549 const std::string
& service_path
,
550 DBusMethodCallStatus call_status
,
551 const base::DictionaryValue
& properties
) {
552 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
553 NET_LOG_ERROR("Failed to get IP Config properties",
554 base::StringPrintf("%s: %d",
555 service_path
.c_str(), call_status
));
558 UpdateIPConfigProperty(service_path
, properties
, shill::kAddressProperty
);
559 UpdateIPConfigProperty(service_path
, properties
, shill::kNameServersProperty
);
560 UpdateIPConfigProperty(service_path
, properties
, shill::kPrefixlenProperty
);
561 UpdateIPConfigProperty(service_path
, properties
, shill::kGatewayProperty
);
562 UpdateIPConfigProperty(service_path
, properties
,
563 shill::kWebProxyAutoDiscoveryUrlProperty
);
566 void ShillPropertyHandler::UpdateIPConfigProperty(
567 const std::string
& service_path
,
568 const base::DictionaryValue
& properties
,
569 const char* property
) {
570 const base::Value
* value
;
571 if (!properties
.GetWithoutPathExpansion(property
, &value
)) {
572 LOG(ERROR
) << "Failed to get IPConfig property: " << property
573 << ", for: " << service_path
;
576 listener_
->UpdateNetworkServiceProperty(
577 service_path
, NetworkState::IPConfigProperty(property
), *value
);
580 void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
581 const std::string
& path
,
582 const std::string
& key
,
583 const base::Value
& value
) {
584 listener_
->UpdateDeviceProperty(path
, key
, value
);
587 } // namespace internal
588 } // namespace chromeos