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 virtual ~ShillPropertyObserver() {
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 virtual 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::ConnectToBestServices() const {
211 NET_LOG_EVENT("ConnectToBestServices", "");
212 shill_manager_
->ConnectToBestServices(
213 base::Bind(&base::DoNothing
),
214 base::Bind(&network_handler::ShillErrorCallbackFunction
,
215 "ConnectToBestServices Failed",
216 "", network_handler::ErrorCallback()));
219 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type
,
220 const std::string
& path
) {
221 if (pending_updates_
[type
].find(path
) != pending_updates_
[type
].end())
222 return; // Update already requested.
224 NET_LOG_DEBUG("Request Properties: " + ManagedState::TypeToString(type
),
226 pending_updates_
[type
].insert(path
);
227 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
228 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
229 dbus::ObjectPath(path
),
230 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
231 AsWeakPtr(), type
, path
));
232 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
233 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
234 dbus::ObjectPath(path
),
235 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
236 AsWeakPtr(), type
, path
));
242 void ShillPropertyHandler::OnPropertyChanged(const std::string
& key
,
243 const base::Value
& value
) {
244 ManagerPropertyChanged(key
, value
);
245 CheckPendingStateListUpdates(key
);
248 //------------------------------------------------------------------------------
251 void ShillPropertyHandler::ManagerPropertiesCallback(
252 DBusMethodCallStatus call_status
,
253 const base::DictionaryValue
& properties
) {
254 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
255 NET_LOG_ERROR("ManagerPropertiesCallback",
256 base::StringPrintf("Failed: %d", call_status
));
259 NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
260 for (base::DictionaryValue::Iterator
iter(properties
);
261 !iter
.IsAtEnd(); iter
.Advance()) {
262 ManagerPropertyChanged(iter
.key(), iter
.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::kServiceCompleteListProperty
) &&
272 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0) {
273 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
275 if ((key
.empty() || key
== shill::kDevicesProperty
) &&
276 pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0) {
277 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
281 void ShillPropertyHandler::ManagerPropertyChanged(const std::string
& key
,
282 const base::Value
& value
) {
283 NET_LOG_DEBUG("ManagerPropertyChanged", key
);
284 if (key
== shill::kDefaultServiceProperty
) {
285 std::string service_path
;
286 value
.GetAsString(&service_path
);
287 listener_
->DefaultNetworkServiceChanged(service_path
);
288 } else if (key
== shill::kServiceCompleteListProperty
) {
289 const base::ListValue
* vlist
= GetListValue(key
, value
);
291 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
292 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
293 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
295 } else if (key
== shill::kDevicesProperty
) {
296 const base::ListValue
* vlist
= GetListValue(key
, value
);
298 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
299 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
300 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
302 } else if (key
== shill::kAvailableTechnologiesProperty
) {
303 const base::ListValue
* vlist
= GetListValue(key
, value
);
305 UpdateAvailableTechnologies(*vlist
);
306 } else if (key
== shill::kEnabledTechnologiesProperty
) {
307 const base::ListValue
* vlist
= GetListValue(key
, value
);
309 UpdateEnabledTechnologies(*vlist
);
310 } else if (key
== shill::kUninitializedTechnologiesProperty
) {
311 const base::ListValue
* vlist
= GetListValue(key
, value
);
313 UpdateUninitializedTechnologies(*vlist
);
314 } else if (key
== shill::kProfilesProperty
) {
315 listener_
->ProfileListChanged();
316 } else if (key
== shill::kCheckPortalListProperty
) {
317 std::string check_portal_list
;
318 if (value
.GetAsString(&check_portal_list
))
319 listener_
->CheckPortalListChanged(check_portal_list
);
321 VLOG(2) << "Ignored Manager Property: " << key
;
325 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type
,
326 const base::ListValue
& entries
) {
327 std::set
<std::string
>& requested_updates
= requested_updates_
[type
];
328 std::set
<std::string
> new_requested_updates
;
329 NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type
),
330 base::StringPrintf("%" PRIuS
, entries
.GetSize()));
331 for (base::ListValue::const_iterator iter
= entries
.begin();
332 iter
!= entries
.end(); ++iter
) {
334 (*iter
)->GetAsString(&path
);
338 // We add a special case for devices here to work around an issue in shill
339 // that prevents it from sending property changed signals for cellular
340 // devices (see crbug.com/321854).
341 if (type
== ManagedState::MANAGED_TYPE_DEVICE
||
342 requested_updates
.find(path
) == requested_updates
.end()) {
343 RequestProperties(type
, path
);
345 new_requested_updates
.insert(path
);
347 requested_updates
.swap(new_requested_updates
);
350 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type
,
351 const base::ListValue
& entries
) {
352 ShillPropertyObserverMap
& observer_map
=
353 (type
== ManagedState::MANAGED_TYPE_NETWORK
)
354 ? observed_networks_
: observed_devices_
;
355 ShillPropertyObserverMap new_observed
;
356 for (auto* entry
: entries
) {
358 entry
->GetAsString(&path
);
361 auto iter
= observer_map
.find(path
);
362 ShillPropertyObserver
* observer
;
363 if (iter
!= observer_map
.end()) {
364 observer
= iter
->second
;
366 // Create an observer for future updates.
367 observer
= new ShillPropertyObserver(
368 type
, path
, base::Bind(
369 &ShillPropertyHandler::PropertyChangedCallback
, AsWeakPtr()));
371 auto result
= new_observed
.insert(std::make_pair(path
, observer
));
372 if (!result
.second
) {
373 LOG(ERROR
) << path
<< " is duplicated in the list.";
376 observer_map
.erase(path
);
377 // Limit the number of observed services.
378 if (new_observed
.size() >= kMaxObserved
)
381 // Delete network service observers still in observer_map.
382 for (auto& observer
: observer_map
) {
383 delete observer
.second
;
385 observer_map
.swap(new_observed
);
388 void ShillPropertyHandler::UpdateAvailableTechnologies(
389 const base::ListValue
& technologies
) {
390 std::stringstream technologies_str
;
391 technologies_str
<< technologies
;
392 NET_LOG_EVENT("AvailableTechnologies:", technologies_str
.str());
393 available_technologies_
.clear();
394 for (base::ListValue::const_iterator iter
= technologies
.begin();
395 iter
!= technologies
.end(); ++iter
) {
396 std::string technology
;
397 (*iter
)->GetAsString(&technology
);
398 DCHECK(!technology
.empty());
399 available_technologies_
.insert(technology
);
401 listener_
->TechnologyListChanged();
404 void ShillPropertyHandler::UpdateEnabledTechnologies(
405 const base::ListValue
& technologies
) {
406 std::stringstream technologies_str
;
407 technologies_str
<< technologies
;
408 NET_LOG_EVENT("EnabledTechnologies:", technologies_str
.str());
409 enabled_technologies_
.clear();
410 for (base::ListValue::const_iterator iter
= technologies
.begin();
411 iter
!= technologies
.end(); ++iter
) {
412 std::string technology
;
413 (*iter
)->GetAsString(&technology
);
414 DCHECK(!technology
.empty());
415 enabled_technologies_
.insert(technology
);
416 enabling_technologies_
.erase(technology
);
418 listener_
->TechnologyListChanged();
421 void ShillPropertyHandler::UpdateUninitializedTechnologies(
422 const base::ListValue
& technologies
) {
423 std::stringstream technologies_str
;
424 technologies_str
<< technologies
;
425 NET_LOG_EVENT("UninitializedTechnologies:", technologies_str
.str());
426 uninitialized_technologies_
.clear();
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 uninitialized_technologies_
.insert(technology
);
434 listener_
->TechnologyListChanged();
437 void ShillPropertyHandler::EnableTechnologyFailed(
438 const std::string
& technology
,
439 const network_handler::ErrorCallback
& error_callback
,
440 const std::string
& dbus_error_name
,
441 const std::string
& dbus_error_message
) {
442 enabling_technologies_
.erase(technology
);
443 network_handler::ShillErrorCallbackFunction(
444 "EnableTechnology Failed",
445 technology
, error_callback
,
446 dbus_error_name
, dbus_error_message
);
449 void ShillPropertyHandler::GetPropertiesCallback(
450 ManagedState::ManagedType type
,
451 const std::string
& path
,
452 DBusMethodCallStatus call_status
,
453 const base::DictionaryValue
& properties
) {
454 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type
),
456 pending_updates_
[type
].erase(path
);
457 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
458 // The shill service no longer exists. This can happen when a network
460 NET_LOG_DEBUG("Failed to get properties",
461 base::StringPrintf("%s: %d", path
.c_str(), call_status
));
464 listener_
->UpdateManagedStateProperties(type
, path
, properties
);
466 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
467 // Request IPConfig properties.
468 const base::Value
* value
;
469 if (properties
.GetWithoutPathExpansion(shill::kIPConfigProperty
, &value
))
470 RequestIPConfig(type
, path
, *value
);
471 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
472 // Clear and request IPConfig properties for each entry in IPConfigs.
473 const base::Value
* value
;
474 if (properties
.GetWithoutPathExpansion(shill::kIPConfigsProperty
, &value
))
475 RequestIPConfigsList(type
, path
, *value
);
478 // Notify the listener only when all updates for that type have completed.
479 if (pending_updates_
[type
].size() == 0)
480 listener_
->ManagedStateListChanged(type
);
483 void ShillPropertyHandler::PropertyChangedCallback(
484 ManagedState::ManagedType type
,
485 const std::string
& path
,
486 const std::string
& key
,
487 const base::Value
& value
) {
488 if (type
== ManagedState::MANAGED_TYPE_NETWORK
&&
489 key
== shill::kIPConfigProperty
) {
490 RequestIPConfig(type
, path
, value
);
491 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
&&
492 key
== shill::kIPConfigsProperty
) {
493 RequestIPConfigsList(type
, path
, value
);
496 if (type
== ManagedState::MANAGED_TYPE_NETWORK
)
497 listener_
->UpdateNetworkServiceProperty(path
, key
, value
);
498 else if (type
== ManagedState::MANAGED_TYPE_DEVICE
)
499 listener_
->UpdateDeviceProperty(path
, key
, value
);
504 void ShillPropertyHandler::RequestIPConfig(
505 ManagedState::ManagedType type
,
506 const std::string
& path
,
507 const base::Value
& ip_config_path_value
) {
508 std::string ip_config_path
;
509 if (!ip_config_path_value
.GetAsString(&ip_config_path
) ||
510 ip_config_path
.empty()) {
511 NET_LOG_ERROR("Invalid IPConfig", path
);
514 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
515 dbus::ObjectPath(ip_config_path
),
516 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
517 AsWeakPtr(), type
, path
, ip_config_path
));
520 void ShillPropertyHandler::RequestIPConfigsList(
521 ManagedState::ManagedType type
,
522 const std::string
& path
,
523 const base::Value
& ip_config_list_value
) {
524 const base::ListValue
* ip_configs
;
525 if (!ip_config_list_value
.GetAsList(&ip_configs
))
527 for (base::ListValue::const_iterator iter
= ip_configs
->begin();
528 iter
!= ip_configs
->end(); ++iter
) {
529 RequestIPConfig(type
, path
, **iter
);
533 void ShillPropertyHandler::GetIPConfigCallback(
534 ManagedState::ManagedType type
,
535 const std::string
& path
,
536 const std::string
& ip_config_path
,
537 DBusMethodCallStatus call_status
,
538 const base::DictionaryValue
& properties
) {
539 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
540 // IP Config properties not availabe. Shill will emit a property change
543 base::StringPrintf("Failed to get IP Config properties: %s: %d",
544 ip_config_path
.c_str(), call_status
), path
);
547 NET_LOG_EVENT("IP Config properties received", path
);
548 listener_
->UpdateIPConfigProperties(type
, path
, ip_config_path
, properties
);
551 } // namespace internal
552 } // namespace chromeos