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/string_util.h"
11 #include "base/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_service_client.h"
18 #include "chromeos/network/network_event_log.h"
19 #include "chromeos/network/network_state.h"
20 #include "dbus/object_path.h"
21 #include "third_party/cros_system_api/dbus/service_constants.h"
25 const char kLogModule
[] = "ShillPropertyHandler";
27 // Limit the number of services or devices we observe. Since they are listed in
28 // priority order, it should be reasonable to ignore services past this.
29 const size_t kMaxObserved
= 100;
31 const base::ListValue
* GetListValue(const std::string
& key
,
32 const base::Value
& value
) {
33 const base::ListValue
* vlist
= NULL
;
34 if (!value
.GetAsList(&vlist
)) {
35 LOG(ERROR
) << "Error parsing key as list: " << key
;
46 // Class to manage Shill service property changed observers. Observers are
47 // added on construction and removed on destruction. Runs the handler when
48 // OnPropertyChanged is called.
49 class ShillPropertyObserver
: public ShillPropertyChangedObserver
{
51 typedef base::Callback
<void(ManagedState::ManagedType type
,
52 const std::string
& service
,
53 const std::string
& name
,
54 const base::Value
& value
)> Handler
;
56 ShillPropertyObserver(ManagedState::ManagedType type
,
57 const std::string
& path
,
58 const Handler
& handler
)
62 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
63 DBusThreadManager::Get()->GetShillServiceClient()->
64 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
65 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
66 DBusThreadManager::Get()->GetShillDeviceClient()->
67 AddPropertyChangedObserver(dbus::ObjectPath(path_
), this);
73 virtual ~ShillPropertyObserver() {
74 if (type_
== ManagedState::MANAGED_TYPE_NETWORK
) {
75 DBusThreadManager::Get()->GetShillServiceClient()->
76 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
77 } else if (type_
== ManagedState::MANAGED_TYPE_DEVICE
) {
78 DBusThreadManager::Get()->GetShillDeviceClient()->
79 RemovePropertyChangedObserver(dbus::ObjectPath(path_
), this);
85 // ShillPropertyChangedObserver overrides.
86 virtual void OnPropertyChanged(const std::string
& key
,
87 const base::Value
& value
) OVERRIDE
{
88 handler_
.Run(type_
, path_
, key
, value
);
92 ManagedState::ManagedType type_
;
96 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver
);
99 //------------------------------------------------------------------------------
100 // ShillPropertyHandler
102 ShillPropertyHandler::ShillPropertyHandler(Listener
* listener
)
103 : listener_(listener
),
104 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
107 ShillPropertyHandler::~ShillPropertyHandler() {
108 // Delete network service observers.
109 STLDeleteContainerPairSecondPointers(
110 observed_networks_
.begin(), observed_networks_
.end());
111 STLDeleteContainerPairSecondPointers(
112 observed_devices_
.begin(), observed_devices_
.end());
113 CHECK(shill_manager_
== DBusThreadManager::Get()->GetShillManagerClient());
114 shill_manager_
->RemovePropertyChangedObserver(this);
117 void ShillPropertyHandler::Init() {
118 shill_manager_
->GetProperties(
119 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback
,
121 shill_manager_
->AddPropertyChangedObserver(this);
124 bool ShillPropertyHandler::IsTechnologyAvailable(
125 const std::string
& technology
) const {
126 return available_technologies_
.count(technology
) != 0;
129 bool ShillPropertyHandler::IsTechnologyEnabled(
130 const std::string
& technology
) const {
131 return enabled_technologies_
.count(technology
) != 0;
134 bool ShillPropertyHandler::IsTechnologyEnabling(
135 const std::string
& technology
) const {
136 return enabling_technologies_
.count(technology
) != 0;
139 bool ShillPropertyHandler::IsTechnologyUninitialized(
140 const std::string
& technology
) const {
141 return uninitialized_technologies_
.count(technology
) != 0;
144 void ShillPropertyHandler::SetTechnologyEnabled(
145 const std::string
& technology
,
147 const network_handler::ErrorCallback
& error_callback
) {
149 enabling_technologies_
.insert(technology
);
150 shill_manager_
->EnableTechnology(
152 base::Bind(&base::DoNothing
),
153 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed
,
154 AsWeakPtr(), technology
, error_callback
));
156 // Imediately clear locally from enabled and enabling lists.
157 enabled_technologies_
.erase(technology
);
158 enabling_technologies_
.erase(technology
);
159 shill_manager_
->DisableTechnology(
161 base::Bind(&base::DoNothing
),
162 base::Bind(&network_handler::ShillErrorCallbackFunction
,
163 kLogModule
, technology
, error_callback
));
167 void ShillPropertyHandler::RequestScan() const {
168 shill_manager_
->RequestScan(
170 base::Bind(&base::DoNothing
),
171 base::Bind(&network_handler::ShillErrorCallbackFunction
,
172 kLogModule
, "", network_handler::ErrorCallback()));
175 void ShillPropertyHandler::ConnectToBestServices() const {
176 network_event_log::AddEntry(kLogModule
, "ConnectToBestServices", "");
177 shill_manager_
->ConnectToBestServices(
178 base::Bind(&base::DoNothing
),
179 base::Bind(&network_handler::ShillErrorCallbackFunction
,
180 kLogModule
, "", network_handler::ErrorCallback()));
183 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type
,
184 const std::string
& path
) {
185 if (pending_updates_
[type
].find(path
) != pending_updates_
[type
].end())
186 return; // Update already requested.
188 pending_updates_
[type
].insert(path
);
189 if (type
== ManagedState::MANAGED_TYPE_NETWORK
) {
190 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
191 dbus::ObjectPath(path
),
192 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
193 AsWeakPtr(), type
, path
));
194 } else if (type
== ManagedState::MANAGED_TYPE_DEVICE
) {
195 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
196 dbus::ObjectPath(path
),
197 base::Bind(&ShillPropertyHandler::GetPropertiesCallback
,
198 AsWeakPtr(), type
, path
));
204 void ShillPropertyHandler::OnPropertyChanged(const std::string
& key
,
205 const base::Value
& value
) {
206 if (ManagerPropertyChanged(key
, value
))
207 listener_
->ManagerPropertyChanged();
208 // If the service watch or device list changed and there are no pending
209 // updates, signal the state list changed callback.
210 if ((key
== flimflam::kServiceWatchListProperty
) &&
211 pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0) {
212 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
214 if (key
== flimflam::kDevicesProperty
&&
215 pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0) {
216 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
220 //------------------------------------------------------------------------------
223 void ShillPropertyHandler::ManagerPropertiesCallback(
224 DBusMethodCallStatus call_status
,
225 const base::DictionaryValue
& properties
) {
226 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
227 LOG(ERROR
) << "Failed to get Manager properties:" << call_status
;
231 bool update_service_list
= false;
232 for (base::DictionaryValue::Iterator
iter(properties
);
233 !iter
.IsAtEnd(); iter
.Advance()) {
234 // Defer updating Services until all other properties have been updated.
235 if (iter
.key() == flimflam::kServicesProperty
)
236 update_service_list
= true;
238 notify
|= ManagerPropertyChanged(iter
.key(), iter
.value());
240 // Now update the service list which can safely assume other properties have
241 // been initially set.
242 if (update_service_list
) {
243 const base::Value
* value
= NULL
;
244 if (properties
.GetWithoutPathExpansion(flimflam::kServicesProperty
, &value
))
245 notify
|= ManagerPropertyChanged(flimflam::kServicesProperty
, *value
);
248 listener_
->ManagerPropertyChanged();
249 // If there are no pending updates, signal the state list changed callbacks.
250 if (pending_updates_
[ManagedState::MANAGED_TYPE_NETWORK
].size() == 0)
251 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK
);
252 if (pending_updates_
[ManagedState::MANAGED_TYPE_DEVICE
].size() == 0)
253 listener_
->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE
);
256 bool ShillPropertyHandler::ManagerPropertyChanged(const std::string
& key
,
257 const base::Value
& value
) {
258 bool notify_manager_changed
= false;
259 if (key
== flimflam::kServicesProperty
) {
260 const base::ListValue
* vlist
= GetListValue(key
, value
);
262 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
263 } else if (key
== flimflam::kServiceWatchListProperty
) {
264 const base::ListValue
* vlist
= GetListValue(key
, value
);
266 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK
, *vlist
);
268 } else if (key
== flimflam::kDevicesProperty
) {
269 const ListValue
* vlist
= GetListValue(key
, value
);
271 listener_
->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
272 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE
, *vlist
);
274 } else if (key
== flimflam::kAvailableTechnologiesProperty
) {
275 const base::ListValue
* vlist
= GetListValue(key
, value
);
277 UpdateAvailableTechnologies(*vlist
);
278 notify_manager_changed
= true;
280 } else if (key
== flimflam::kEnabledTechnologiesProperty
) {
281 const base::ListValue
* vlist
= GetListValue(key
, value
);
283 UpdateEnabledTechnologies(*vlist
);
284 notify_manager_changed
= true;
286 } else if (key
== shill::kUninitializedTechnologiesProperty
) {
287 const base::ListValue
* vlist
= GetListValue(key
, value
);
289 UpdateUninitializedTechnologies(*vlist
);
290 notify_manager_changed
= true;
292 } else if (key
== flimflam::kProfilesProperty
) {
293 listener_
->ProfileListChanged();
295 return notify_manager_changed
;
298 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type
,
299 const base::ListValue
& entries
) {
300 ShillPropertyObserverMap
& observer_map
=
301 (type
== ManagedState::MANAGED_TYPE_NETWORK
)
302 ? observed_networks_
: observed_devices_
;
303 ShillPropertyObserverMap new_observed
;
304 for (base::ListValue::const_iterator iter1
= entries
.begin();
305 iter1
!= entries
.end(); ++iter1
) {
307 (*iter1
)->GetAsString(&path
);
310 ShillPropertyObserverMap::iterator iter2
= observer_map
.find(path
);
311 if (iter2
!= observer_map
.end()) {
312 new_observed
[path
] = iter2
->second
;
314 // Request an update.
315 RequestProperties(type
, path
);
316 // Create an observer for future updates.
317 new_observed
[path
] = new ShillPropertyObserver(
318 type
, path
, base::Bind(
319 &ShillPropertyHandler::PropertyChangedCallback
, AsWeakPtr()));
320 network_event_log::AddEntry(kLogModule
, "StartObserving", path
);
322 observer_map
.erase(path
);
323 // Limit the number of observed services.
324 if (new_observed
.size() >= kMaxObserved
)
327 // Delete network service observers still in observer_map.
328 for (ShillPropertyObserverMap::iterator iter
= observer_map
.begin();
329 iter
!= observer_map
.end(); ++iter
) {
330 network_event_log::AddEntry(kLogModule
, "StopObserving", iter
->first
);
333 observer_map
.swap(new_observed
);
336 void ShillPropertyHandler::UpdateAvailableTechnologies(
337 const base::ListValue
& technologies
) {
338 available_technologies_
.clear();
339 network_event_log::AddEntry(
340 kLogModule
, "AvailableTechnologiesChanged",
341 base::StringPrintf("Size: %"PRIuS
, technologies
.GetSize()));
342 for (base::ListValue::const_iterator iter
= technologies
.begin();
343 iter
!= technologies
.end(); ++iter
) {
344 std::string technology
;
345 (*iter
)->GetAsString(&technology
);
346 DCHECK(!technology
.empty());
347 available_technologies_
.insert(technology
);
351 void ShillPropertyHandler::UpdateEnabledTechnologies(
352 const base::ListValue
& technologies
) {
353 enabled_technologies_
.clear();
354 network_event_log::AddEntry(
355 kLogModule
, "EnabledTechnologiesChanged",
356 base::StringPrintf("Size: %"PRIuS
, technologies
.GetSize()));
357 for (base::ListValue::const_iterator iter
= technologies
.begin();
358 iter
!= technologies
.end(); ++iter
) {
359 std::string technology
;
360 (*iter
)->GetAsString(&technology
);
361 DCHECK(!technology
.empty());
362 enabled_technologies_
.insert(technology
);
363 enabling_technologies_
.erase(technology
);
367 void ShillPropertyHandler::UpdateUninitializedTechnologies(
368 const base::ListValue
& technologies
) {
369 uninitialized_technologies_
.clear();
370 network_event_log::AddEntry(
371 kLogModule
, "UninitializedTechnologiesChanged",
372 base::StringPrintf("Size: %"PRIuS
, technologies
.GetSize()));
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 uninitialized_technologies_
.insert(technology
);
382 void ShillPropertyHandler::EnableTechnologyFailed(
383 const std::string
& technology
,
384 const network_handler::ErrorCallback
& error_callback
,
385 const std::string
& error_name
,
386 const std::string
& error_message
) {
387 enabling_technologies_
.erase(technology
);
388 network_handler::ShillErrorCallbackFunction(
389 kLogModule
, technology
, error_callback
, error_name
, error_message
);
392 void ShillPropertyHandler::GetPropertiesCallback(
393 ManagedState::ManagedType type
,
394 const std::string
& path
,
395 DBusMethodCallStatus call_status
,
396 const base::DictionaryValue
& properties
) {
397 VLOG(2) << "GetPropertiesCallback: " << type
<< " : " << path
;
398 pending_updates_
[type
].erase(path
);
399 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
400 LOG(ERROR
) << "Failed to get properties for: " << path
401 << ": " << call_status
;
404 listener_
->UpdateManagedStateProperties(type
, path
, properties
);
406 if (properties
.HasKey(shill::kIPConfigProperty
)) {
407 // Since this is the first time we received properties for this network,
408 // also request its IPConfig parameters.
409 std::string ip_config_path
;
410 if (properties
.GetString(shill::kIPConfigProperty
, &ip_config_path
)) {
411 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
412 dbus::ObjectPath(ip_config_path
),
413 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
418 // Notify the listener only when all updates for that type have completed.
419 if (pending_updates_
[type
].size() == 0)
420 listener_
->ManagedStateListChanged(type
);
423 void ShillPropertyHandler::PropertyChangedCallback(
424 ManagedState::ManagedType type
,
425 const std::string
& path
,
426 const std::string
& key
,
427 const base::Value
& value
) {
428 if (type
== ManagedState::MANAGED_TYPE_NETWORK
)
429 NetworkServicePropertyChangedCallback(path
, key
, value
);
430 else if (type
== ManagedState::MANAGED_TYPE_DEVICE
)
431 NetworkDevicePropertyChangedCallback(path
, key
, value
);
436 void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
437 const std::string
& path
,
438 const std::string
& key
,
439 const base::Value
& value
) {
440 if (key
== shill::kIPConfigProperty
) {
441 // Request the IPConfig for the network and update network properties
442 // when the request completes.
443 std::string ip_config_path
;
444 value
.GetAsString(&ip_config_path
);
445 DCHECK(!ip_config_path
.empty());
446 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
447 dbus::ObjectPath(ip_config_path
),
448 base::Bind(&ShillPropertyHandler::GetIPConfigCallback
,
451 listener_
->UpdateNetworkServiceProperty(path
, key
, value
);
455 void ShillPropertyHandler::GetIPConfigCallback(
456 const std::string
& service_path
,
457 DBusMethodCallStatus call_status
,
458 const base::DictionaryValue
& properties
) {
459 if (call_status
!= DBUS_METHOD_CALL_SUCCESS
) {
460 LOG(ERROR
) << "Failed to get IP Config properties for: " << service_path
;
463 const base::Value
* ip_address
;
464 if (!properties
.GetWithoutPathExpansion(flimflam::kAddressProperty
,
466 LOG(ERROR
) << "Failed to get IP Address property for: " << service_path
;
469 listener_
->UpdateNetworkServiceProperty(
471 NetworkState::IPConfigProperty(flimflam::kAddressProperty
),
474 const base::Value
* dns_servers
= NULL
;
475 if (!properties
.GetWithoutPathExpansion(
476 flimflam::kNameServersProperty
, &dns_servers
)) {
477 LOG(ERROR
) << "Failed to get Name servers property for: " << service_path
;
480 listener_
->UpdateNetworkServiceProperty(
482 NetworkState::IPConfigProperty(flimflam::kNameServersProperty
),
486 void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
487 const std::string
& path
,
488 const std::string
& key
,
489 const base::Value
& value
) {
490 listener_
->UpdateDeviceProperty(path
, key
, value
);
493 } // namespace internal
494 } // namespace chromeos