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/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_state.h"
20 #include "components/device_event_log/device_event_log.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
)
58 : type_(type
), path_(path
), handler_(handler
) {
59 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
60 DVLOG(2) << "ShillPropertyObserver: Network: " << path
;
61 DBusThreadManager::Get()
62 ->GetShillServiceClient()
63 ->AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
64 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
65 DVLOG(2) << "ShillPropertyObserver: Device: " << path
;
66 DBusThreadManager::Get()
67 ->GetShillDeviceClient()
68 ->AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
74 ~ShillPropertyObserver() override
{
75 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
76 DBusThreadManager::Get()
77 ->GetShillServiceClient()
78 ->RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
79 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
80 DBusThreadManager::Get()
81 ->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(observed_networks_
.begin(),
113 observed_networks_
.end());
114 STLDeleteContainerPairSecondPointers(observed_devices_
.begin(),
115 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(base::Bind(
128 &ShillPropertyHandler::ManagerPropertiesCallback
, AsWeakPtr()));
131 bool ShillPropertyHandler::IsTechnologyAvailable(
132 const std::string
& technology
) const {
133 return available_technologies_
.count(technology
) != 0;
136 bool ShillPropertyHandler::IsTechnologyEnabled(
137 const std::string
& technology
) const {
138 return enabled_technologies_
.count(technology
) != 0;
141 bool ShillPropertyHandler::IsTechnologyEnabling(
142 const std::string
& technology
) const {
143 return enabling_technologies_
.count(technology
) != 0;
146 bool ShillPropertyHandler::IsTechnologyUninitialized(
147 const std::string
& technology
) const {
148 return uninitialized_technologies_
.count(technology
) != 0;
151 void ShillPropertyHandler::SetTechnologyEnabled(
152 const std::string
& technology
,
154 const network_handler::ErrorCallback
& error_callback
) {
156 enabling_technologies_
.insert(technology
);
157 shill_manager_
->EnableTechnology(
158 technology
, base::Bind(&base::DoNothing
),
159 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed
, AsWeakPtr(),
160 technology
, error_callback
));
162 // Immediately clear locally from enabled and enabling lists.
163 enabled_technologies_
.erase(technology
);
164 enabling_technologies_
.erase(technology
);
165 shill_manager_
->DisableTechnology(
166 technology
, base::Bind(&base::DoNothing
),
167 base::Bind(&network_handler::ShillErrorCallbackFunction
,
168 "SetTechnologyEnabled Failed", 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
, value
, base::Bind(&base::DoNothing
),
177 base::Bind(&network_handler::ShillErrorCallbackFunction
,
178 "SetCheckPortalList Failed", "Manager",
179 network_handler::ErrorCallback()));
182 void ShillPropertyHandler::SetWakeOnLanEnabled(bool enabled
) {
183 base::FundamentalValue
value(enabled
);
184 shill_manager_
->SetProperty(
185 shill::kWakeOnLanEnabledProperty
, value
, base::Bind(&base::DoNothing
),
186 base::Bind(&network_handler::ShillErrorCallbackFunction
,
187 "SetWakeOnLanEnabled Failed", "Manager",
188 network_handler::ErrorCallback()));
191 void ShillPropertyHandler::RequestScan() const {
192 shill_manager_
->RequestScan(
193 "", base::Bind(&base::DoNothing
),
194 base::Bind(&network_handler::ShillErrorCallbackFunction
,
195 "RequestScan Failed", "", network_handler::ErrorCallback()));
198 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type
,
199 const std::string
& path
) {
200 if (pending_updates_
[type
].find(path
) != pending_updates_
[type
].end())
201 return; // Update already requested.
203 NET_LOG(DEBUG
) << "Request Properties: " << ManagedState::TypeToString(type
)
205 pending_updates_
[type
].insert(path
);
206 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
207 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
208 dbus::ObjectPath(path
),
209 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
, AsWeakPtr(),
211 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
212 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
213 dbus::ObjectPath(path
),
214 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
, AsWeakPtr(),
221 void ShillPropertyHandler::OnPropertyChanged(const std::string
& key
,
222 const base::Value
& value
) {
223 ManagerPropertyChanged(key
, value
);
224 CheckPendingStateListUpdates(key
);
227 //------------------------------------------------------------------------------
230 void ShillPropertyHandler::ManagerPropertiesCallback(
231 DBusMethodCallStatus call_status
,
232 const base::DictionaryValue
& properties
) {
233 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
234 NET_LOG(ERROR
) << "ManagerPropertiesCallback Failed: " << call_status
;
237 NET_LOG(EVENT
) << "ManagerPropertiesCallback: Success";
238 for (base::DictionaryValue::Iterator
iter(properties
); !iter
.IsAtEnd();
240 ManagerPropertyChanged(iter
.key(), iter
.value());
243 CheckPendingStateListUpdates("");
246 void ShillPropertyHandler::CheckPendingStateListUpdates(
247 const std::string
& key
) {
248 // Once there are no pending updates, signal the state list changed callbacks.
249 if ((key
.empty() || key
== shill::kServiceCompleteListProperty
) &&
250 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0) {
251 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
253 if ((key
.empty() || key
== shill::kDevicesProperty
) &&
254 pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0) {
255 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
259 void ShillPropertyHandler::ManagerPropertyChanged(const std::string
& key
,
260 const base::Value
& value
) {
261 if (key
== shill::kDefaultServiceProperty
) {
262 std::string service_path
;
263 value
.GetAsString(&service_path
);
264 NET_LOG(EVENT
) << "Manager.DefaultService = " << service_path
;
265 listener_
->DefaultNetworkServiceChanged(service_path
);
268 NET_LOG(DEBUG
) << "ManagerPropertyChanged: " << key
;
269 if (key
== shill::kServiceCompleteListProperty
) {
270 const base::ListValue
* vlist
= GetListValue(key
, value
);
272 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
273 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
274 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
276 } else if (key
== shill::kDevicesProperty
) {
277 const base::ListValue
* vlist
= GetListValue(key
, value
);
279 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
280 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
281 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
283 } else if (key
== shill::kAvailableTechnologiesProperty
) {
284 const base::ListValue
* vlist
= GetListValue(key
, value
);
286 UpdateAvailableTechnologies(*vlist
);
287 } else if (key
== shill::kEnabledTechnologiesProperty
) {
288 const base::ListValue
* vlist
= GetListValue(key
, value
);
290 UpdateEnabledTechnologies(*vlist
);
291 } else if (key
== shill::kUninitializedTechnologiesProperty
) {
292 const base::ListValue
* vlist
= GetListValue(key
, value
);
294 UpdateUninitializedTechnologies(*vlist
);
295 } else if (key
== shill::kProfilesProperty
) {
296 listener_
->ProfileListChanged();
297 } else if (key
== shill::kCheckPortalListProperty
) {
298 std::string check_portal_list
;
299 if (value
.GetAsString(&check_portal_list
))
300 listener_
->CheckPortalListChanged(check_portal_list
);
302 VLOG(2) << "Ignored Manager Property: " << key
;
306 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type
,
307 const base::ListValue
& entries
) {
308 std::set
<std::string
>& requested_updates
= requested_updates_
[type
];
309 std::set
<std::string
> new_requested_updates
;
310 NET_LOG(DEBUG
) << "UpdateProperties: " << ManagedState::TypeToString(type
)
311 << ": " << entries
.GetSize();
312 for (base::ListValue::const_iterator iter
= entries
.begin();
313 iter
!= entries
.end(); ++iter
) {
315 (*iter
)->GetAsString(&path
);
319 // We add a special case for devices here to work around an issue in shill
320 // that prevents it from sending property changed signals for cellular
321 // devices (see crbug.com/321854).
322 if (type
== ManagedState::MANAGED_TYPE_DEVICE
||
323 requested_updates
.find(path
) == requested_updates
.end()) {
324 RequestProperties(type
, path
);
326 new_requested_updates
.insert(path
);
328 requested_updates
.swap(new_requested_updates
);
331 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type
,
332 const base::ListValue
& entries
) {
333 ShillPropertyObserverMap
& observer_map
=
334 (type
== ManagedState::MANAGED_TYPE_NETWORK
) ? observed_networks_
336 ShillPropertyObserverMap new_observed
;
337 for (auto* entry
: entries
) {
339 entry
->GetAsString(&path
);
342 auto iter
= observer_map
.find(path
);
343 ShillPropertyObserver
* observer
;
344 if (iter
!= observer_map
.end()) {
345 observer
= iter
->second
;
347 // Create an observer for future updates.
348 observer
= new ShillPropertyObserver(
349 type
, path
, base::Bind(&ShillPropertyHandler::PropertyChangedCallback
,
352 auto result
= new_observed
.insert(std::make_pair(path
, observer
));
353 if (!result
.second
) {
354 LOG(ERROR
) << path
<< " is duplicated in the list.";
357 observer_map
.erase(path
);
358 // Limit the number of observed services.
359 if (new_observed
.size() >= kMaxObserved
)
362 // Delete network service observers still in observer_map.
363 for (auto& observer
: observer_map
) {
364 delete observer
.second
;
366 observer_map
.swap(new_observed
);
369 void ShillPropertyHandler::UpdateAvailableTechnologies(
370 const base::ListValue
& technologies
) {
371 NET_LOG(EVENT
) << "AvailableTechnologies:" << technologies
;
372 available_technologies_
.clear();
373 for (base::ListValue::const_iterator iter
= technologies
.begin();
374 iter
!= technologies
.end(); ++iter
) {
375 std::string technology
;
376 (*iter
)->GetAsString(&technology
);
377 DCHECK(!technology
.empty());
378 available_technologies_
.insert(technology
);
380 listener_
->TechnologyListChanged();
383 void ShillPropertyHandler::UpdateEnabledTechnologies(
384 const base::ListValue
& technologies
) {
385 NET_LOG(EVENT
) << "EnabledTechnologies:" << technologies
;
386 enabled_technologies_
.clear();
387 for (base::ListValue::const_iterator iter
= technologies
.begin();
388 iter
!= technologies
.end(); ++iter
) {
389 std::string technology
;
390 (*iter
)->GetAsString(&technology
);
391 DCHECK(!technology
.empty());
392 enabled_technologies_
.insert(technology
);
393 enabling_technologies_
.erase(technology
);
395 listener_
->TechnologyListChanged();
398 void ShillPropertyHandler::UpdateUninitializedTechnologies(
399 const base::ListValue
& technologies
) {
400 NET_LOG(EVENT
) << "UninitializedTechnologies:" << technologies
;
401 uninitialized_technologies_
.clear();
402 for (base::ListValue::const_iterator iter
= technologies
.begin();
403 iter
!= technologies
.end(); ++iter
) {
404 std::string technology
;
405 (*iter
)->GetAsString(&technology
);
406 DCHECK(!technology
.empty());
407 uninitialized_technologies_
.insert(technology
);
409 listener_
->TechnologyListChanged();
412 void ShillPropertyHandler::EnableTechnologyFailed(
413 const std::string
& technology
,
414 const network_handler::ErrorCallback
& error_callback
,
415 const std::string
& dbus_error_name
,
416 const std::string
& dbus_error_message
) {
417 enabling_technologies_
.erase(technology
);
418 network_handler::ShillErrorCallbackFunction(
419 "EnableTechnology Failed", technology
, error_callback
, dbus_error_name
,
423 void ShillPropertyHandler::GetPropertiesCallback(
424 ManagedState::ManagedType type
,
425 const std::string
& path
,
426 DBusMethodCallStatus call_status
,
427 const base::DictionaryValue
& properties
) {
428 NET_LOG(DEBUG
) << "GetPropertiesCallback: "
429 << ManagedState::TypeToString(type
) << " For: " << path
;
430 pending_updates_
[type
].erase(path
);
431 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
432 // The shill service no longer exists. This can happen when a network
434 NET_LOG(DEBUG
) << "Failed to get properties for: " << path
<< ": "
438 listener_
->UpdateManagedStateProperties(type
, path
, properties
);
440 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
441 // Request IPConfig properties.
442 const base::Value
* value
;
443 if (properties
.GetWithoutPathExpansion(shill::kIPConfigProperty
, &value
))
444 RequestIPConfig(type
, path
, *value
);
445 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
446 // Clear and request IPConfig properties for each entry in IPConfigs.
447 const base::Value
* value
;
448 if (properties
.GetWithoutPathExpansion(shill::kIPConfigsProperty
, &value
))
449 RequestIPConfigsList(type
, path
, *value
);
452 // Notify the listener only when all updates for that type have completed.
453 if (pending_updates_
[type
].size() == 0)
454 listener_
->ManagedStateListChanged(type
);
457 void ShillPropertyHandler::PropertyChangedCallback(
458 ManagedState::ManagedType type
,
459 const std::string
& path
,
460 const std::string
& key
,
461 const base::Value
& value
) {
462 if (type
== ManagedState::MANAGED_TYPE_NETWORK
&&
463 key
== shill::kIPConfigProperty
) {
464 RequestIPConfig(type
, path
, value
);
465 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
&&
466 key
== shill::kIPConfigsProperty
) {
467 RequestIPConfigsList(type
, path
, value
);
470 if (type
== ManagedState::MANAGED_TYPE_NETWORK
)
471 listener_
->UpdateNetworkServiceProperty(path
, key
, value
);
472 else if (type
== ManagedState::MANAGED_TYPE_DEVICE
)
473 listener_
->UpdateDeviceProperty(path
, key
, value
);
478 void ShillPropertyHandler::RequestIPConfig(
479 ManagedState::ManagedType type
,
480 const std::string
& path
,
481 const base::Value
& ip_config_path_value
) {
482 std::string ip_config_path
;
483 if (!ip_config_path_value
.GetAsString(&ip_config_path
) ||
484 ip_config_path
.empty()) {
485 NET_LOG(ERROR
) << "Invalid IPConfig: " << path
;
488 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
489 dbus::ObjectPath(ip_config_path
),
490 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
, AsWeakPtr(), type
,
491 path
, ip_config_path
));
494 void ShillPropertyHandler::RequestIPConfigsList(
495 ManagedState::ManagedType type
,
496 const std::string
& path
,
497 const base::Value
& ip_config_list_value
) {
498 const base::ListValue
* ip_configs
;
499 if (!ip_config_list_value
.GetAsList(&ip_configs
))
501 for (base::ListValue::const_iterator iter
= ip_configs
->begin();
502 iter
!= ip_configs
->end(); ++iter
) {
503 RequestIPConfig(type
, path
, **iter
);
507 void ShillPropertyHandler::GetIPConfigCallback(
508 ManagedState::ManagedType type
,
509 const std::string
& path
,
510 const std::string
& ip_config_path
,
511 DBusMethodCallStatus call_status
,
512 const base::DictionaryValue
& properties
) {
513 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
514 // IP Config properties not availabe. Shill will emit a property change
516 NET_LOG(EVENT
) << "Failed to get IP Config properties: " << ip_config_path
517 << ": " << call_status
<< ", For: " << path
;
520 NET_LOG(EVENT
) << "IP Config properties received: " << path
;
521 listener_
->UpdateIPConfigProperties(type
, path
, ip_config_path
, properties
);
524 } // namespace internal
525 } // namespace chromeos