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.
4
5 #include "chromeos/network/shill_property_handler.h"
6
7 #include <sstream>
8
9 #include "base/bind.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"
25
26 namespace {
27
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;
31
GetListValue(const std::string & key,const base::Value & value)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;
37 return NULL;
38 }
39 return vlist;
40 }
41
42 } // namespace
43
44 namespace chromeos {
45 namespace internal {
46
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 {
51 public:
52 typedef base::Callback<void(ManagedState::ManagedType type,
53 const std::string& service,
54 const std::string& name,
55 const base::Value& value)> Handler;
56
ShillPropertyObserver(ManagedState::ManagedType type,const std::string & path,const Handler & handler)57 ShillPropertyObserver(ManagedState::ManagedType type,
58 const std::string& path,
59 const Handler& handler)
60 : type_(type),
61 path_(path),
62 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);
71 } else {
72 NOTREACHED();
73 }
74 }
75
~ShillPropertyObserver()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);
83 } else {
84 NOTREACHED();
85 }
86 }
87
88 // ShillPropertyChangedObserver overrides.
OnPropertyChanged(const std::string & key,const base::Value & value)89 virtual void OnPropertyChanged(const std::string& key,
90 const base::Value& value) OVERRIDE {
91 handler_.Run(type_, path_, key, value);
92 }
93
94 private:
95 ManagedState::ManagedType type_;
96 std::string path_;
97 Handler handler_;
98
99 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver);
100 };
101
102 //------------------------------------------------------------------------------
103 // ShillPropertyHandler
104
ShillPropertyHandler(Listener * listener)105 ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
106 : listener_(listener),
107 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
108 }
109
~ShillPropertyHandler()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);
118 }
119
Init()120 void ShillPropertyHandler::Init() {
121 UpdateManagerProperties();
122 shill_manager_->AddPropertyChangedObserver(this);
123 }
124
UpdateManagerProperties()125 void ShillPropertyHandler::UpdateManagerProperties() {
126 NET_LOG_EVENT("UpdateManagerProperties", "");
127 shill_manager_->GetProperties(
128 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
129 AsWeakPtr()));
130 }
131
IsTechnologyAvailable(const std::string & technology) const132 bool ShillPropertyHandler::IsTechnologyAvailable(
133 const std::string& technology) const {
134 return available_technologies_.count(technology) != 0;
135 }
136
IsTechnologyEnabled(const std::string & technology) const137 bool ShillPropertyHandler::IsTechnologyEnabled(
138 const std::string& technology) const {
139 return enabled_technologies_.count(technology) != 0;
140 }
141
IsTechnologyEnabling(const std::string & technology) const142 bool ShillPropertyHandler::IsTechnologyEnabling(
143 const std::string& technology) const {
144 return enabling_technologies_.count(technology) != 0;
145 }
146
IsTechnologyUninitialized(const std::string & technology) const147 bool ShillPropertyHandler::IsTechnologyUninitialized(
148 const std::string& technology) const {
149 return uninitialized_technologies_.count(technology) != 0;
150 }
151
SetTechnologyEnabled(const std::string & technology,bool enabled,const network_handler::ErrorCallback & error_callback)152 void ShillPropertyHandler::SetTechnologyEnabled(
153 const std::string& technology,
154 bool enabled,
155 const network_handler::ErrorCallback& error_callback) {
156 if (enabled) {
157 enabling_technologies_.insert(technology);
158 shill_manager_->EnableTechnology(
159 technology,
160 base::Bind(&base::DoNothing),
161 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed,
162 AsWeakPtr(), technology, error_callback));
163 } else {
164 // Immediately clear locally from enabled and enabling lists.
165 enabled_technologies_.erase(technology);
166 enabling_technologies_.erase(technology);
167 shill_manager_->DisableTechnology(
168 technology,
169 base::Bind(&base::DoNothing),
170 base::Bind(&network_handler::ShillErrorCallbackFunction,
171 "SetTechnologyEnabled Failed",
172 technology, error_callback));
173 }
174 }
175
SetCheckPortalList(const std::string & check_portal_list)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,
181 value,
182 base::Bind(&base::DoNothing),
183 base::Bind(&network_handler::ShillErrorCallbackFunction,
184 "SetCheckPortalList Failed",
185 "", network_handler::ErrorCallback()));
186 }
187
RequestScan() const188 void ShillPropertyHandler::RequestScan() const {
189 shill_manager_->RequestScan(
190 "",
191 base::Bind(&base::DoNothing),
192 base::Bind(&network_handler::ShillErrorCallbackFunction,
193 "RequestScan Failed",
194 "", network_handler::ErrorCallback()));
195 }
196
ConnectToBestServices() const197 void ShillPropertyHandler::ConnectToBestServices() const {
198 NET_LOG_EVENT("ConnectToBestServices", "");
199 shill_manager_->ConnectToBestServices(
200 base::Bind(&base::DoNothing),
201 base::Bind(&network_handler::ShillErrorCallbackFunction,
202 "ConnectToBestServices Failed",
203 "", network_handler::ErrorCallback()));
204 }
205
RequestProperties(ManagedState::ManagedType type,const std::string & path)206 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
207 const std::string& path) {
208 if (pending_updates_[type].find(path) != pending_updates_[type].end())
209 return; // Update already requested.
210
211 NET_LOG_DEBUG("Request Properties: " + ManagedState::TypeToString(type),
212 path);
213 pending_updates_[type].insert(path);
214 if (type == ManagedState::MANAGED_TYPE_NETWORK) {
215 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
216 dbus::ObjectPath(path),
217 base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
218 AsWeakPtr(), type, path));
219 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
220 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
221 dbus::ObjectPath(path),
222 base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
223 AsWeakPtr(), type, path));
224 } else {
225 NOTREACHED();
226 }
227 }
228
OnPropertyChanged(const std::string & key,const base::Value & value)229 void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
230 const base::Value& value) {
231 ManagerPropertyChanged(key, value);
232 CheckPendingStateListUpdates(key);
233 }
234
235 //------------------------------------------------------------------------------
236 // Private methods
237
ManagerPropertiesCallback(DBusMethodCallStatus call_status,const base::DictionaryValue & properties)238 void ShillPropertyHandler::ManagerPropertiesCallback(
239 DBusMethodCallStatus call_status,
240 const base::DictionaryValue& properties) {
241 if (call_status != DBUS_METHOD_CALL_SUCCESS) {
242 NET_LOG_ERROR("ManagerPropertiesCallback",
243 base::StringPrintf("Failed: %d", call_status));
244 return;
245 }
246 NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
247 for (base::DictionaryValue::Iterator iter(properties);
248 !iter.IsAtEnd(); iter.Advance()) {
249 ManagerPropertyChanged(iter.key(), iter.value());
250 }
251
252 CheckPendingStateListUpdates("");
253 }
254
CheckPendingStateListUpdates(const std::string & key)255 void ShillPropertyHandler::CheckPendingStateListUpdates(
256 const std::string& key) {
257 // Once there are no pending updates, signal the state list changed callbacks.
258 if ((key.empty() || key == shill::kServiceCompleteListProperty) &&
259 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
260 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
261 }
262 if ((key.empty() || key == shill::kDevicesProperty) &&
263 pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
264 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
265 }
266 }
267
ManagerPropertyChanged(const std::string & key,const base::Value & value)268 void ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
269 const base::Value& value) {
270 NET_LOG_DEBUG("ManagerPropertyChanged", key);
271 if (key == shill::kDefaultServiceProperty) {
272 std::string service_path;
273 value.GetAsString(&service_path);
274 listener_->DefaultNetworkServiceChanged(service_path);
275 } else if (key == shill::kServiceCompleteListProperty) {
276 const base::ListValue* vlist = GetListValue(key, value);
277 if (vlist) {
278 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
279 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
280 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
281 }
282 } else if (key == shill::kDevicesProperty) {
283 const base::ListValue* vlist = GetListValue(key, value);
284 if (vlist) {
285 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
286 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
287 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
288 }
289 } else if (key == shill::kAvailableTechnologiesProperty) {
290 const base::ListValue* vlist = GetListValue(key, value);
291 if (vlist)
292 UpdateAvailableTechnologies(*vlist);
293 } else if (key == shill::kEnabledTechnologiesProperty) {
294 const base::ListValue* vlist = GetListValue(key, value);
295 if (vlist)
296 UpdateEnabledTechnologies(*vlist);
297 } else if (key == shill::kUninitializedTechnologiesProperty) {
298 const base::ListValue* vlist = GetListValue(key, value);
299 if (vlist)
300 UpdateUninitializedTechnologies(*vlist);
301 } else if (key == shill::kProfilesProperty) {
302 listener_->ProfileListChanged();
303 } else if (key == shill::kCheckPortalListProperty) {
304 std::string check_portal_list;
305 if (value.GetAsString(&check_portal_list))
306 listener_->CheckPortalListChanged(check_portal_list);
307 } else {
308 VLOG(2) << "Ignored Manager Property: " << key;
309 }
310 }
311
UpdateProperties(ManagedState::ManagedType type,const base::ListValue & entries)312 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type,
313 const base::ListValue& entries) {
314 std::set<std::string>& requested_updates = requested_updates_[type];
315 std::set<std::string> new_requested_updates;
316 NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type),
317 base::StringPrintf("%" PRIuS, entries.GetSize()));
318 for (base::ListValue::const_iterator iter = entries.begin();
319 iter != entries.end(); ++iter) {
320 std::string path;
321 (*iter)->GetAsString(&path);
322 if (path.empty())
323 continue;
324
325 // We add a special case for devices here to work around an issue in shill
326 // that prevents it from sending property changed signals for cellular
327 // devices (see crbug.com/321854).
328 if (type == ManagedState::MANAGED_TYPE_DEVICE ||
329 requested_updates.find(path) == requested_updates.end()) {
330 RequestProperties(type, path);
331 }
332 new_requested_updates.insert(path);
333 }
334 requested_updates.swap(new_requested_updates);
335 }
336
UpdateObserved(ManagedState::ManagedType type,const base::ListValue & entries)337 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
338 const base::ListValue& entries) {
339 ShillPropertyObserverMap& observer_map =
340 (type == ManagedState::MANAGED_TYPE_NETWORK)
341 ? observed_networks_ : observed_devices_;
342 ShillPropertyObserverMap new_observed;
343 for (base::ListValue::const_iterator iter1 = entries.begin();
344 iter1 != entries.end(); ++iter1) {
345 std::string path;
346 (*iter1)->GetAsString(&path);
347 if (path.empty())
348 continue;
349 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
350 if (iter2 != observer_map.end()) {
351 new_observed[path] = iter2->second;
352 } else {
353 // Create an observer for future updates.
354 new_observed[path] = new ShillPropertyObserver(
355 type, path, base::Bind(
356 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
357 }
358 observer_map.erase(path);
359 // Limit the number of observed services.
360 if (new_observed.size() >= kMaxObserved)
361 break;
362 }
363 // Delete network service observers still in observer_map.
364 for (ShillPropertyObserverMap::iterator iter = observer_map.begin();
365 iter != observer_map.end(); ++iter) {
366 delete iter->second;
367 }
368 observer_map.swap(new_observed);
369 }
370
UpdateAvailableTechnologies(const base::ListValue & technologies)371 void ShillPropertyHandler::UpdateAvailableTechnologies(
372 const base::ListValue& technologies) {
373 std::stringstream technologies_str;
374 technologies_str << technologies;
375 NET_LOG_EVENT("AvailableTechnologies:", technologies_str.str());
376 available_technologies_.clear();
377 for (base::ListValue::const_iterator iter = technologies.begin();
378 iter != technologies.end(); ++iter) {
379 std::string technology;
380 (*iter)->GetAsString(&technology);
381 DCHECK(!technology.empty());
382 available_technologies_.insert(technology);
383 }
384 listener_->TechnologyListChanged();
385 }
386
UpdateEnabledTechnologies(const base::ListValue & technologies)387 void ShillPropertyHandler::UpdateEnabledTechnologies(
388 const base::ListValue& technologies) {
389 std::stringstream technologies_str;
390 technologies_str << technologies;
391 NET_LOG_EVENT("EnabledTechnologies:", technologies_str.str());
392 enabled_technologies_.clear();
393 for (base::ListValue::const_iterator iter = technologies.begin();
394 iter != technologies.end(); ++iter) {
395 std::string technology;
396 (*iter)->GetAsString(&technology);
397 DCHECK(!technology.empty());
398 enabled_technologies_.insert(technology);
399 enabling_technologies_.erase(technology);
400 }
401 listener_->TechnologyListChanged();
402 }
403
UpdateUninitializedTechnologies(const base::ListValue & technologies)404 void ShillPropertyHandler::UpdateUninitializedTechnologies(
405 const base::ListValue& technologies) {
406 std::stringstream technologies_str;
407 technologies_str << technologies;
408 NET_LOG_EVENT("UninitializedTechnologies:", technologies_str.str());
409 uninitialized_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 uninitialized_technologies_.insert(technology);
416 }
417 listener_->TechnologyListChanged();
418 }
419
EnableTechnologyFailed(const std::string & technology,const network_handler::ErrorCallback & error_callback,const std::string & dbus_error_name,const std::string & dbus_error_message)420 void ShillPropertyHandler::EnableTechnologyFailed(
421 const std::string& technology,
422 const network_handler::ErrorCallback& error_callback,
423 const std::string& dbus_error_name,
424 const std::string& dbus_error_message) {
425 enabling_technologies_.erase(technology);
426 network_handler::ShillErrorCallbackFunction(
427 "EnableTechnology Failed",
428 technology, error_callback,
429 dbus_error_name, dbus_error_message);
430 }
431
GetPropertiesCallback(ManagedState::ManagedType type,const std::string & path,DBusMethodCallStatus call_status,const base::DictionaryValue & properties)432 void ShillPropertyHandler::GetPropertiesCallback(
433 ManagedState::ManagedType type,
434 const std::string& path,
435 DBusMethodCallStatus call_status,
436 const base::DictionaryValue& properties) {
437 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type),
438 path);
439 pending_updates_[type].erase(path);
440 if (call_status != DBUS_METHOD_CALL_SUCCESS) {
441 // The shill service no longer exists. This can happen when a network
442 // has been removed.
443 NET_LOG_DEBUG("Failed to get properties",
444 base::StringPrintf("%s: %d", path.c_str(), call_status));
445 return;
446 }
447 listener_->UpdateManagedStateProperties(type, path, properties);
448
449 if (type == ManagedState::MANAGED_TYPE_NETWORK) {
450 // Request IPConfig properties.
451 const base::Value* value;
452 if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value))
453 RequestIPConfig(type, path, *value);
454 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
455 // Clear and request IPConfig properties for each entry in IPConfigs.
456 const base::Value* value;
457 if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value))
458 RequestIPConfigsList(type, path, *value);
459 }
460
461 // Notify the listener only when all updates for that type have completed.
462 if (pending_updates_[type].size() == 0)
463 listener_->ManagedStateListChanged(type);
464 }
465
PropertyChangedCallback(ManagedState::ManagedType type,const std::string & path,const std::string & key,const base::Value & value)466 void ShillPropertyHandler::PropertyChangedCallback(
467 ManagedState::ManagedType type,
468 const std::string& path,
469 const std::string& key,
470 const base::Value& value) {
471 if (type == ManagedState::MANAGED_TYPE_NETWORK &&
472 key == shill::kIPConfigProperty) {
473 RequestIPConfig(type, path, value);
474 } else if (type == ManagedState::MANAGED_TYPE_DEVICE &&
475 key == shill::kIPConfigsProperty) {
476 RequestIPConfigsList(type, path, value);
477 }
478
479 if (type == ManagedState::MANAGED_TYPE_NETWORK)
480 listener_->UpdateNetworkServiceProperty(path, key, value);
481 else if (type == ManagedState::MANAGED_TYPE_DEVICE)
482 listener_->UpdateDeviceProperty(path, key, value);
483 else
484 NOTREACHED();
485 }
486
RequestIPConfig(ManagedState::ManagedType type,const std::string & path,const base::Value & ip_config_path_value)487 void ShillPropertyHandler::RequestIPConfig(
488 ManagedState::ManagedType type,
489 const std::string& path,
490 const base::Value& ip_config_path_value) {
491 std::string ip_config_path;
492 if (!ip_config_path_value.GetAsString(&ip_config_path) ||
493 ip_config_path.empty()) {
494 NET_LOG_ERROR("Invalid IPConfig", path);
495 return;
496 }
497 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
498 dbus::ObjectPath(ip_config_path),
499 base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
500 AsWeakPtr(), type, path, ip_config_path));
501 }
502
RequestIPConfigsList(ManagedState::ManagedType type,const std::string & path,const base::Value & ip_config_list_value)503 void ShillPropertyHandler::RequestIPConfigsList(
504 ManagedState::ManagedType type,
505 const std::string& path,
506 const base::Value& ip_config_list_value) {
507 const base::ListValue* ip_configs;
508 if (!ip_config_list_value.GetAsList(&ip_configs))
509 return;
510 for (base::ListValue::const_iterator iter = ip_configs->begin();
511 iter != ip_configs->end(); ++iter) {
512 RequestIPConfig(type, path, **iter);
513 }
514 }
515
GetIPConfigCallback(ManagedState::ManagedType type,const std::string & path,const std::string & ip_config_path,DBusMethodCallStatus call_status,const base::DictionaryValue & properties)516 void ShillPropertyHandler::GetIPConfigCallback(
517 ManagedState::ManagedType type,
518 const std::string& path,
519 const std::string& ip_config_path,
520 DBusMethodCallStatus call_status,
521 const base::DictionaryValue& properties) {
522 if (call_status != DBUS_METHOD_CALL_SUCCESS) {
523 // IP Config properties not availabe. Shill will emit a property change
524 // when they are.
525 NET_LOG_EVENT(
526 base::StringPrintf("Failed to get IP Config properties: %s: %d",
527 ip_config_path.c_str(), call_status), path);
528 return;
529 }
530 NET_LOG_EVENT("IP Config properties received", path);
531 listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties);
532 }
533
534 } // namespace internal
535 } // namespace chromeos
536