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"
10 #include "base/format_macros.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/dbus/shill_device_client.h"
17 #include "chromeos/dbus/shill_ipconfig_client.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_event_log.h"
22 #include "chromeos/network/network_state.h"
23 #include "dbus/object_path.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
28 // Limit the number of services or devices we observe. Since they are listed in
29 // priority order, it should be reasonable to ignore services past this.
30 const size_t kMaxObserved
= 100;
32 const base::ListValue
* GetListValue(const std::string
& key
,
33 const base::Value
& value
) {
34 const base::ListValue
* vlist
= NULL
;
35 if (!value
.GetAsList(&vlist
)) {
36 LOG(ERROR
) << "Error parsing key as list: " << key
;
47 // Class to manage Shill service property changed observers. Observers are
48 // added on construction and removed on destruction. Runs the handler when
49 // OnPropertyChanged is called.
50 class ShillPropertyObserver
: public ShillPropertyChangedObserver
{
52 typedef base::Callback
<void(ManagedState::ManagedType type
,
53 const std::string
& service
,
54 const std::string
& name
,
55 const base::Value
& value
)> Handler
;
57 ShillPropertyObserver(ManagedState::ManagedType type
,
58 const std::string
& path
,
59 const Handler
& handler
)
63 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
64 DVLOG(2) << "ShillPropertyObserver: Network: " << path
;
65 DBusThreadManager::Get()->GetShillServiceClient()->
66 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
67 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
68 DVLOG(2) << "ShillPropertyObserver: Device: " << path
;
69 DBusThreadManager::Get()->GetShillDeviceClient()->
70 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
76 ~ShillPropertyObserver() override
{
77 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
78 DBusThreadManager::Get()->GetShillServiceClient()->
79 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
80 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
81 DBusThreadManager::Get()->GetShillDeviceClient()->
82 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
88 // ShillPropertyChangedObserver overrides.
89 void OnPropertyChanged(const std::string
& key
,
90 const base::Value
& value
) override
{
91 handler_
.Run(type_
, path_
, key
, value
);
95 ManagedState::ManagedType type_
;
99 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver
);
102 //------------------------------------------------------------------------------
103 // ShillPropertyHandler
105 ShillPropertyHandler::ShillPropertyHandler(Listener
* listener
)
106 : listener_(listener
),
107 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
110 ShillPropertyHandler::~ShillPropertyHandler() {
111 // Delete network service observers.
112 STLDeleteContainerPairSecondPointers(
113 observed_networks_
.begin(), observed_networks_
.end());
114 STLDeleteContainerPairSecondPointers(
115 observed_devices_
.begin(), observed_devices_
.end());
116 CHECK(shill_manager_
== DBusThreadManager::Get()->GetShillManagerClient());
117 shill_manager_
->RemovePropertyChangedObserver(this);
120 void ShillPropertyHandler::Init() {
121 UpdateManagerProperties();
122 shill_manager_
->AddPropertyChangedObserver(this);
125 void ShillPropertyHandler::UpdateManagerProperties() {
126 NET_LOG_EVENT("UpdateManagerProperties", "");
127 shill_manager_
->GetProperties(
128 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback
,
132 bool ShillPropertyHandler::IsTechnologyAvailable(
133 const std::string
& technology
) const {
134 return available_technologies_
.count(technology
) != 0;
137 bool ShillPropertyHandler::IsTechnologyEnabled(
138 const std::string
& technology
) const {
139 return enabled_technologies_
.count(technology
) != 0;
142 bool ShillPropertyHandler::IsTechnologyEnabling(
143 const std::string
& technology
) const {
144 return enabling_technologies_
.count(technology
) != 0;
147 bool ShillPropertyHandler::IsTechnologyUninitialized(
148 const std::string
& technology
) const {
149 return uninitialized_technologies_
.count(technology
) != 0;
152 void ShillPropertyHandler::SetTechnologyEnabled(
153 const std::string
& technology
,
155 const network_handler::ErrorCallback
& error_callback
) {
157 enabling_technologies_
.insert(technology
);
158 shill_manager_
->EnableTechnology(
160 base::Bind(&base::DoNothing
),
161 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed
,
162 AsWeakPtr(), technology
, error_callback
));
164 // Immediately clear locally from enabled and enabling lists.
165 enabled_technologies_
.erase(technology
);
166 enabling_technologies_
.erase(technology
);
167 shill_manager_
->DisableTechnology(
169 base::Bind(&base::DoNothing
),
170 base::Bind(&network_handler::ShillErrorCallbackFunction
,
171 "SetTechnologyEnabled Failed",
172 technology
, error_callback
));
176 void ShillPropertyHandler::SetCheckPortalList(
177 const std::string
& check_portal_list
) {
178 base::StringValue
value(check_portal_list
);
179 shill_manager_
->SetProperty(
180 shill::kCheckPortalListProperty
,
182 base::Bind(&base::DoNothing
),
183 base::Bind(&network_handler::ShillErrorCallbackFunction
,
184 "SetCheckPortalList Failed",
186 network_handler::ErrorCallback()));
189 void ShillPropertyHandler::SetWakeOnLanEnabled(bool enabled
) {
190 base::FundamentalValue
value(enabled
);
191 shill_manager_
->SetProperty(
192 shill::kWakeOnLanEnabledProperty
,
194 base::Bind(&base::DoNothing
),
195 base::Bind(&network_handler::ShillErrorCallbackFunction
,
196 "SetWakeOnLanEnabled Failed",
198 network_handler::ErrorCallback()));
201 void ShillPropertyHandler::RequestScan() const {
202 shill_manager_
->RequestScan(
204 base::Bind(&base::DoNothing
),
205 base::Bind(&network_handler::ShillErrorCallbackFunction
,
206 "RequestScan Failed",
207 "", network_handler::ErrorCallback()));
210 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type
,
211 const std::string
& path
) {
212 if (pending_updates_
[type
].find(path
) != pending_updates_
[type
].end())
213 return; // Update already requested.
215 NET_LOG_DEBUG("Request Properties: " + ManagedState::TypeToString(type
),
217 pending_updates_
[type
].insert(path
);
218 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
219 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
220 dbus::ObjectPath(path
),
221 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
222 AsWeakPtr(), type
, path
));
223 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
224 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
225 dbus::ObjectPath(path
),
226 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
227 AsWeakPtr(), type
, path
));
233 void ShillPropertyHandler::OnPropertyChanged(const std::string
& key
,
234 const base::Value
& value
) {
235 ManagerPropertyChanged(key
, value
);
236 CheckPendingStateListUpdates(key
);
239 //------------------------------------------------------------------------------
242 void ShillPropertyHandler::ManagerPropertiesCallback(
243 DBusMethodCallStatus call_status
,
244 const base::DictionaryValue
& properties
) {
245 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
246 NET_LOG_ERROR("ManagerPropertiesCallback",
247 base::StringPrintf("Failed: %d", call_status
));
250 NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
251 for (base::DictionaryValue::Iterator
iter(properties
);
252 !iter
.IsAtEnd(); iter
.Advance()) {
253 ManagerPropertyChanged(iter
.key(), iter
.value());
256 CheckPendingStateListUpdates("");
259 void ShillPropertyHandler::CheckPendingStateListUpdates(
260 const std::string
& key
) {
261 // Once there are no pending updates, signal the state list changed callbacks.
262 if ((key
.empty() || key
== shill::kServiceCompleteListProperty
) &&
263 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0) {
264 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
266 if ((key
.empty() || key
== shill::kDevicesProperty
) &&
267 pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0) {
268 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
272 void ShillPropertyHandler::ManagerPropertyChanged(const std::string
& key
,
273 const base::Value
& value
) {
274 NET_LOG_DEBUG("ManagerPropertyChanged", key
);
275 if (key
== shill::kDefaultServiceProperty
) {
276 std::string service_path
;
277 value
.GetAsString(&service_path
);
278 listener_
->DefaultNetworkServiceChanged(service_path
);
279 } else if (key
== shill::kServiceCompleteListProperty
) {
280 const base::ListValue
* vlist
= GetListValue(key
, value
);
282 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
283 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
284 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
286 } else if (key
== shill::kDevicesProperty
) {
287 const base::ListValue
* vlist
= GetListValue(key
, value
);
289 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
290 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
291 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
293 } else if (key
== shill::kAvailableTechnologiesProperty
) {
294 const base::ListValue
* vlist
= GetListValue(key
, value
);
296 UpdateAvailableTechnologies(*vlist
);
297 } else if (key
== shill::kEnabledTechnologiesProperty
) {
298 const base::ListValue
* vlist
= GetListValue(key
, value
);
300 UpdateEnabledTechnologies(*vlist
);
301 } else if (key
== shill::kUninitializedTechnologiesProperty
) {
302 const base::ListValue
* vlist
= GetListValue(key
, value
);
304 UpdateUninitializedTechnologies(*vlist
);
305 } else if (key
== shill::kProfilesProperty
) {
306 listener_
->ProfileListChanged();
307 } else if (key
== shill::kCheckPortalListProperty
) {
308 std::string check_portal_list
;
309 if (value
.GetAsString(&check_portal_list
))
310 listener_
->CheckPortalListChanged(check_portal_list
);
312 VLOG(2) << "Ignored Manager Property: " << key
;
316 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type
,
317 const base::ListValue
& entries
) {
318 std::set
<std::string
>& requested_updates
= requested_updates_
[type
];
319 std::set
<std::string
> new_requested_updates
;
320 NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type
),
321 base::StringPrintf("%" PRIuS
, entries
.GetSize()));
322 for (base::ListValue::const_iterator iter
= entries
.begin();
323 iter
!= entries
.end(); ++iter
) {
325 (*iter
)->GetAsString(&path
);
329 // We add a special case for devices here to work around an issue in shill
330 // that prevents it from sending property changed signals for cellular
331 // devices (see crbug.com/321854).
332 if (type
== ManagedState::MANAGED_TYPE_DEVICE
||
333 requested_updates
.find(path
) == requested_updates
.end()) {
334 RequestProperties(type
, path
);
336 new_requested_updates
.insert(path
);
338 requested_updates
.swap(new_requested_updates
);
341 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type
,
342 const base::ListValue
& entries
) {
343 ShillPropertyObserverMap
& observer_map
=
344 (type
== ManagedState::MANAGED_TYPE_NETWORK
)
345 ? observed_networks_
: observed_devices_
;
346 ShillPropertyObserverMap new_observed
;
347 for (auto* entry
: entries
) {
349 entry
->GetAsString(&path
);
352 auto iter
= observer_map
.find(path
);
353 ShillPropertyObserver
* observer
;
354 if (iter
!= observer_map
.end()) {
355 observer
= iter
->second
;
357 // Create an observer for future updates.
358 observer
= new ShillPropertyObserver(
359 type
, path
, base::Bind(
360 &ShillPropertyHandler::PropertyChangedCallback
, AsWeakPtr()));
362 auto result
= new_observed
.insert(std::make_pair(path
, observer
));
363 if (!result
.second
) {
364 LOG(ERROR
) << path
<< " is duplicated in the list.";
367 observer_map
.erase(path
);
368 // Limit the number of observed services.
369 if (new_observed
.size() >= kMaxObserved
)
372 // Delete network service observers still in observer_map.
373 for (auto& observer
: observer_map
) {
374 delete observer
.second
;
376 observer_map
.swap(new_observed
);
379 void ShillPropertyHandler::UpdateAvailableTechnologies(
380 const base::ListValue
& technologies
) {
381 std::stringstream technologies_str
;
382 technologies_str
<< technologies
;
383 NET_LOG_EVENT("AvailableTechnologies:", technologies_str
.str());
384 available_technologies_
.clear();
385 for (base::ListValue::const_iterator iter
= technologies
.begin();
386 iter
!= technologies
.end(); ++iter
) {
387 std::string technology
;
388 (*iter
)->GetAsString(&technology
);
389 DCHECK(!technology
.empty());
390 available_technologies_
.insert(technology
);
392 listener_
->TechnologyListChanged();
395 void ShillPropertyHandler::UpdateEnabledTechnologies(
396 const base::ListValue
& technologies
) {
397 std::stringstream technologies_str
;
398 technologies_str
<< technologies
;
399 NET_LOG_EVENT("EnabledTechnologies:", technologies_str
.str());
400 enabled_technologies_
.clear();
401 for (base::ListValue::const_iterator iter
= technologies
.begin();
402 iter
!= technologies
.end(); ++iter
) {
403 std::string technology
;
404 (*iter
)->GetAsString(&technology
);
405 DCHECK(!technology
.empty());
406 enabled_technologies_
.insert(technology
);
407 enabling_technologies_
.erase(technology
);
409 listener_
->TechnologyListChanged();
412 void ShillPropertyHandler::UpdateUninitializedTechnologies(
413 const base::ListValue
& technologies
) {
414 std::stringstream technologies_str
;
415 technologies_str
<< technologies
;
416 NET_LOG_EVENT("UninitializedTechnologies:", technologies_str
.str());
417 uninitialized_technologies_
.clear();
418 for (base::ListValue::const_iterator iter
= technologies
.begin();
419 iter
!= technologies
.end(); ++iter
) {
420 std::string technology
;
421 (*iter
)->GetAsString(&technology
);
422 DCHECK(!technology
.empty());
423 uninitialized_technologies_
.insert(technology
);
425 listener_
->TechnologyListChanged();
428 void ShillPropertyHandler::EnableTechnologyFailed(
429 const std::string
& technology
,
430 const network_handler::ErrorCallback
& error_callback
,
431 const std::string
& dbus_error_name
,
432 const std::string
& dbus_error_message
) {
433 enabling_technologies_
.erase(technology
);
434 network_handler::ShillErrorCallbackFunction(
435 "EnableTechnology Failed",
436 technology
, error_callback
,
437 dbus_error_name
, dbus_error_message
);
440 void ShillPropertyHandler::GetPropertiesCallback(
441 ManagedState::ManagedType type
,
442 const std::string
& path
,
443 DBusMethodCallStatus call_status
,
444 const base::DictionaryValue
& properties
) {
445 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type
),
447 pending_updates_
[type
].erase(path
);
448 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
449 // The shill service no longer exists. This can happen when a network
451 NET_LOG_DEBUG("Failed to get properties",
452 base::StringPrintf("%s: %d", path
.c_str(), call_status
));
455 listener_
->UpdateManagedStateProperties(type
, path
, properties
);
457 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
458 // Request IPConfig properties.
459 const base::Value
* value
;
460 if (properties
.GetWithoutPathExpansion(shill::kIPConfigProperty
, &value
))
461 RequestIPConfig(type
, path
, *value
);
462 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
463 // Clear and request IPConfig properties for each entry in IPConfigs.
464 const base::Value
* value
;
465 if (properties
.GetWithoutPathExpansion(shill::kIPConfigsProperty
, &value
))
466 RequestIPConfigsList(type
, path
, *value
);
469 // Notify the listener only when all updates for that type have completed.
470 if (pending_updates_
[type
].size() == 0)
471 listener_
->ManagedStateListChanged(type
);
474 void ShillPropertyHandler::PropertyChangedCallback(
475 ManagedState::ManagedType type
,
476 const std::string
& path
,
477 const std::string
& key
,
478 const base::Value
& value
) {
479 if (type
== ManagedState::MANAGED_TYPE_NETWORK
&&
480 key
== shill::kIPConfigProperty
) {
481 RequestIPConfig(type
, path
, value
);
482 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
&&
483 key
== shill::kIPConfigsProperty
) {
484 RequestIPConfigsList(type
, path
, value
);
487 if (type
== ManagedState::MANAGED_TYPE_NETWORK
)
488 listener_
->UpdateNetworkServiceProperty(path
, key
, value
);
489 else if (type
== ManagedState::MANAGED_TYPE_DEVICE
)
490 listener_
->UpdateDeviceProperty(path
, key
, value
);
495 void ShillPropertyHandler::RequestIPConfig(
496 ManagedState::ManagedType type
,
497 const std::string
& path
,
498 const base::Value
& ip_config_path_value
) {
499 std::string ip_config_path
;
500 if (!ip_config_path_value
.GetAsString(&ip_config_path
) ||
501 ip_config_path
.empty()) {
502 NET_LOG_ERROR("Invalid IPConfig", path
);
505 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
506 dbus::ObjectPath(ip_config_path
),
507 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
508 AsWeakPtr(), type
, path
, ip_config_path
));
511 void ShillPropertyHandler::RequestIPConfigsList(
512 ManagedState::ManagedType type
,
513 const std::string
& path
,
514 const base::Value
& ip_config_list_value
) {
515 const base::ListValue
* ip_configs
;
516 if (!ip_config_list_value
.GetAsList(&ip_configs
))
518 for (base::ListValue::const_iterator iter
= ip_configs
->begin();
519 iter
!= ip_configs
->end(); ++iter
) {
520 RequestIPConfig(type
, path
, **iter
);
524 void ShillPropertyHandler::GetIPConfigCallback(
525 ManagedState::ManagedType type
,
526 const std::string
& path
,
527 const std::string
& ip_config_path
,
528 DBusMethodCallStatus call_status
,
529 const base::DictionaryValue
& properties
) {
530 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
531 // IP Config properties not availabe. Shill will emit a property change
534 base::StringPrintf("Failed to get IP Config properties: %s: %d",
535 ip_config_path
.c_str(), call_status
), path
);
538 NET_LOG_EVENT("IP Config properties received", path
);
539 listener_
->UpdateIPConfigProperties(type
, path
, ip_config_path
, properties
);
542 } // namespace internal
543 } // namespace chromeos