shill_property_handler.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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 26namespace { 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. 30const size_t kMaxObserved = 100; 31 32const 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 44namespace chromeos { 45namespace 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. 50class 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 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 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. 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 105ShillPropertyHandler::ShillPropertyHandler(Listener* listener) 106 : listener_(listener), 107 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) { 108} 109 110ShillPropertyHandler::~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 120void ShillPropertyHandler::Init() { 121 UpdateManagerProperties(); 122 shill_manager_->AddPropertyChangedObserver(this); 123} 124 125void ShillPropertyHandler::UpdateManagerProperties() { 126 NET_LOG_EVENT("UpdateManagerProperties", ""); 127 shill_manager_->GetProperties( 128 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback, 129 AsWeakPtr())); 130} 131 132bool ShillPropertyHandler::IsTechnologyAvailable( 133 const std::string& technology) const { 134 return available_technologies_.count(technology) != 0; 135} 136 137bool ShillPropertyHandler::IsTechnologyEnabled( 138 const std::string& technology) const { 139 return enabled_technologies_.count(technology) != 0; 140} 141 142bool ShillPropertyHandler::IsTechnologyEnabling( 143 const std::string& technology) const { 144 return enabling_technologies_.count(technology) != 0; 145} 146 147bool ShillPropertyHandler::IsTechnologyUninitialized( 148 const std::string& technology) const { 149 return uninitialized_technologies_.count(technology) != 0; 150} 151 152void 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 176void 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 188void 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 197void 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 206void 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 229void 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 238void 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 255void 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 268void 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::kServicesProperty) { 276 const base::ListValue* vlist = GetListValue(key, value); 277 if (vlist) { 278 // We only use the Services property to determine which networks we 279 // observe. This is more convenient and reliable than waiting to revceive 280 // the Visible property of each kServiceCompleteList entry. We limit the 281 // number of observed networks to kMaxObserved. 282 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 283 } 284 } else if (key == shill::kServiceCompleteListProperty) { 285 const base::ListValue* vlist = GetListValue(key, value); 286 if (vlist) { 287 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 288 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 289 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 290 } 291 } else if (key == shill::kDevicesProperty) { 292 const base::ListValue* vlist = GetListValue(key, value); 293 if (vlist) { 294 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 295 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 296 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 297 } 298 } else if (key == shill::kAvailableTechnologiesProperty) { 299 const base::ListValue* vlist = GetListValue(key, value); 300 if (vlist) 301 UpdateAvailableTechnologies(*vlist); 302 } else if (key == shill::kEnabledTechnologiesProperty) { 303 const base::ListValue* vlist = GetListValue(key, value); 304 if (vlist) 305 UpdateEnabledTechnologies(*vlist); 306 } else if (key == shill::kUninitializedTechnologiesProperty) { 307 const base::ListValue* vlist = GetListValue(key, value); 308 if (vlist) 309 UpdateUninitializedTechnologies(*vlist); 310 } else if (key == shill::kProfilesProperty) { 311 listener_->ProfileListChanged(); 312 } else if (key == shill::kCheckPortalListProperty) { 313 std::string check_portal_list; 314 if (value.GetAsString(&check_portal_list)) 315 listener_->CheckPortalListChanged(check_portal_list); 316 } else { 317 VLOG(2) << "Ignored Manager Property: " << key; 318 } 319} 320 321void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type, 322 const base::ListValue& entries) { 323 std::set<std::string>& requested_updates = requested_updates_[type]; 324 std::set<std::string> new_requested_updates; 325 NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type), 326 base::StringPrintf("%" PRIuS, entries.GetSize())); 327 for (base::ListValue::const_iterator iter = entries.begin(); 328 iter != entries.end(); ++iter) { 329 std::string path; 330 (*iter)->GetAsString(&path); 331 if (path.empty()) 332 continue; 333 334 // We add a special case for devices here to work around an issue in shill 335 // that prevents it from sending property changed signals for cellular 336 // devices (see crbug.com/321854). 337 if (type == ManagedState::MANAGED_TYPE_DEVICE || 338 requested_updates.find(path) == requested_updates.end()) { 339 RequestProperties(type, path); 340 } 341 new_requested_updates.insert(path); 342 } 343 requested_updates.swap(new_requested_updates); 344} 345 346void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type, 347 const base::ListValue& entries) { 348 ShillPropertyObserverMap& observer_map = 349 (type == ManagedState::MANAGED_TYPE_NETWORK) 350 ? observed_networks_ : observed_devices_; 351 ShillPropertyObserverMap new_observed; 352 for (base::ListValue::const_iterator iter1 = entries.begin(); 353 iter1 != entries.end(); ++iter1) { 354 std::string path; 355 (*iter1)->GetAsString(&path); 356 if (path.empty()) 357 continue; 358 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path); 359 if (iter2 != observer_map.end()) { 360 new_observed[path] = iter2->second; 361 } else { 362 // Create an observer for future updates. 363 new_observed[path] = new ShillPropertyObserver( 364 type, path, base::Bind( 365 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr())); 366 } 367 observer_map.erase(path); 368 // Limit the number of observed services. 369 if (new_observed.size() >= kMaxObserved) 370 break; 371 } 372 // Delete network service observers still in observer_map. 373 for (ShillPropertyObserverMap::iterator iter = observer_map.begin(); 374 iter != observer_map.end(); ++iter) { 375 delete iter->second; 376 } 377 observer_map.swap(new_observed); 378} 379 380void ShillPropertyHandler::UpdateAvailableTechnologies( 381 const base::ListValue& technologies) { 382 std::stringstream technologies_str; 383 technologies_str << technologies; 384 NET_LOG_EVENT("AvailableTechnologies:", technologies_str.str()); 385 available_technologies_.clear(); 386 for (base::ListValue::const_iterator iter = technologies.begin(); 387 iter != technologies.end(); ++iter) { 388 std::string technology; 389 (*iter)->GetAsString(&technology); 390 DCHECK(!technology.empty()); 391 available_technologies_.insert(technology); 392 } 393 listener_->TechnologyListChanged(); 394} 395 396void ShillPropertyHandler::UpdateEnabledTechnologies( 397 const base::ListValue& technologies) { 398 std::stringstream technologies_str; 399 technologies_str << technologies; 400 NET_LOG_EVENT("EnabledTechnologies:", technologies_str.str()); 401 enabled_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 enabled_technologies_.insert(technology); 408 enabling_technologies_.erase(technology); 409 } 410 listener_->TechnologyListChanged(); 411} 412 413void ShillPropertyHandler::UpdateUninitializedTechnologies( 414 const base::ListValue& technologies) { 415 std::stringstream technologies_str; 416 technologies_str << technologies; 417 NET_LOG_EVENT("UninitializedTechnologies:", technologies_str.str()); 418 uninitialized_technologies_.clear(); 419 for (base::ListValue::const_iterator iter = technologies.begin(); 420 iter != technologies.end(); ++iter) { 421 std::string technology; 422 (*iter)->GetAsString(&technology); 423 DCHECK(!technology.empty()); 424 uninitialized_technologies_.insert(technology); 425 } 426 listener_->TechnologyListChanged(); 427} 428 429void ShillPropertyHandler::EnableTechnologyFailed( 430 const std::string& technology, 431 const network_handler::ErrorCallback& error_callback, 432 const std::string& dbus_error_name, 433 const std::string& dbus_error_message) { 434 enabling_technologies_.erase(technology); 435 network_handler::ShillErrorCallbackFunction( 436 "EnableTechnology Failed", 437 technology, error_callback, 438 dbus_error_name, dbus_error_message); 439} 440 441void ShillPropertyHandler::GetPropertiesCallback( 442 ManagedState::ManagedType type, 443 const std::string& path, 444 DBusMethodCallStatus call_status, 445 const base::DictionaryValue& properties) { 446 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type), 447 path); 448 pending_updates_[type].erase(path); 449 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 450 // The shill service no longer exists. This can happen when a network 451 // has been removed. 452 NET_LOG_DEBUG("Failed to get properties", 453 base::StringPrintf("%s: %d", path.c_str(), call_status)); 454 return; 455 } 456 listener_->UpdateManagedStateProperties(type, path, properties); 457 458 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 459 // Request IPConfig properties. 460 const base::Value* value; 461 if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value)) 462 RequestIPConfig(type, path, *value); 463 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 464 // Clear and request IPConfig properties for each entry in IPConfigs. 465 const base::Value* value; 466 if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value)) 467 RequestIPConfigsList(type, path, *value); 468 } 469 470 // Notify the listener only when all updates for that type have completed. 471 if (pending_updates_[type].size() == 0) 472 listener_->ManagedStateListChanged(type); 473} 474 475void ShillPropertyHandler::PropertyChangedCallback( 476 ManagedState::ManagedType type, 477 const std::string& path, 478 const std::string& key, 479 const base::Value& value) { 480 if (type == ManagedState::MANAGED_TYPE_NETWORK && 481 key == shill::kIPConfigProperty) { 482 RequestIPConfig(type, path, value); 483 } else if (type == ManagedState::MANAGED_TYPE_DEVICE && 484 key == shill::kIPConfigsProperty) { 485 RequestIPConfigsList(type, path, value); 486 } 487 488 if (type == ManagedState::MANAGED_TYPE_NETWORK) 489 listener_->UpdateNetworkServiceProperty(path, key, value); 490 else if (type == ManagedState::MANAGED_TYPE_DEVICE) 491 listener_->UpdateDeviceProperty(path, key, value); 492 else 493 NOTREACHED(); 494} 495 496void ShillPropertyHandler::RequestIPConfig( 497 ManagedState::ManagedType type, 498 const std::string& path, 499 const base::Value& ip_config_path_value) { 500 std::string ip_config_path; 501 if (!ip_config_path_value.GetAsString(&ip_config_path) || 502 ip_config_path.empty()) { 503 NET_LOG_ERROR("Invalid IPConfig", path); 504 return; 505 } 506 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 507 dbus::ObjectPath(ip_config_path), 508 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 509 AsWeakPtr(), type, path, ip_config_path)); 510} 511 512void ShillPropertyHandler::RequestIPConfigsList( 513 ManagedState::ManagedType type, 514 const std::string& path, 515 const base::Value& ip_config_list_value) { 516 const base::ListValue* ip_configs; 517 if (!ip_config_list_value.GetAsList(&ip_configs)) 518 return; 519 for (base::ListValue::const_iterator iter = ip_configs->begin(); 520 iter != ip_configs->end(); ++iter) { 521 RequestIPConfig(type, path, **iter); 522 } 523} 524 525void ShillPropertyHandler::GetIPConfigCallback( 526 ManagedState::ManagedType type, 527 const std::string& path, 528 const std::string& ip_config_path, 529 DBusMethodCallStatus call_status, 530 const base::DictionaryValue& properties) { 531 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 532 NET_LOG_ERROR("Failed to get IP Config properties", 533 base::StringPrintf("%s: %d", path.c_str(), call_status)); 534 return; 535 } 536 NET_LOG_EVENT("IP Config properties received", path); 537 listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties); 538} 539 540} // namespace internal 541} // namespace chromeos 542