shill_property_handler.cc revision 010d83a9304c5a91596085d917d248abff47903a
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 "base/bind.h" 8#include "base/format_macros.h" 9#include "base/stl_util.h" 10#include "base/strings/string_util.h" 11#include "base/strings/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_profile_client.h" 18#include "chromeos/dbus/shill_service_client.h" 19#include "chromeos/network/network_event_log.h" 20#include "chromeos/network/network_state.h" 21#include "dbus/object_path.h" 22#include "third_party/cros_system_api/dbus/service_constants.h" 23 24namespace { 25 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. 28const size_t kMaxObserved = 100; 29 30const 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; 35 return NULL; 36 } 37 return vlist; 38} 39 40} // namespace 41 42namespace chromeos { 43namespace internal { 44 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. 48class ShillPropertyObserver : public ShillPropertyChangedObserver { 49 public: 50 typedef base::Callback<void(ManagedState::ManagedType type, 51 const std::string& service, 52 const std::string& name, 53 const base::Value& value)> Handler; 54 55 ShillPropertyObserver(ManagedState::ManagedType type, 56 const std::string& path, 57 const Handler& handler) 58 : type_(type), 59 path_(path), 60 handler_(handler) { 61 if (type_ == ManagedState::MANAGED_TYPE_NETWORK) { 62 DBusThreadManager::Get()->GetShillServiceClient()-> 63 AddPropertyChangedObserver(dbus::ObjectPath(path_), this); 64 } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) { 65 DBusThreadManager::Get()->GetShillDeviceClient()-> 66 AddPropertyChangedObserver(dbus::ObjectPath(path_), this); 67 } else { 68 NOTREACHED(); 69 } 70 } 71 72 virtual ~ShillPropertyObserver() { 73 if (type_ == ManagedState::MANAGED_TYPE_NETWORK) { 74 DBusThreadManager::Get()->GetShillServiceClient()-> 75 RemovePropertyChangedObserver(dbus::ObjectPath(path_), this); 76 } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) { 77 DBusThreadManager::Get()->GetShillDeviceClient()-> 78 RemovePropertyChangedObserver(dbus::ObjectPath(path_), this); 79 } else { 80 NOTREACHED(); 81 } 82 } 83 84 // ShillPropertyChangedObserver overrides. 85 virtual void OnPropertyChanged(const std::string& key, 86 const base::Value& value) OVERRIDE { 87 handler_.Run(type_, path_, key, value); 88 } 89 90 private: 91 ManagedState::ManagedType type_; 92 std::string path_; 93 Handler handler_; 94 95 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver); 96}; 97 98//------------------------------------------------------------------------------ 99// ShillPropertyHandler 100 101ShillPropertyHandler::ShillPropertyHandler(Listener* listener) 102 : listener_(listener), 103 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) { 104} 105 106ShillPropertyHandler::~ShillPropertyHandler() { 107 // Delete network service observers. 108 STLDeleteContainerPairSecondPointers( 109 observed_networks_.begin(), observed_networks_.end()); 110 STLDeleteContainerPairSecondPointers( 111 observed_devices_.begin(), observed_devices_.end()); 112 CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient()); 113 shill_manager_->RemovePropertyChangedObserver(this); 114} 115 116void ShillPropertyHandler::Init() { 117 UpdateManagerProperties(); 118 shill_manager_->AddPropertyChangedObserver(this); 119} 120 121void ShillPropertyHandler::UpdateManagerProperties() { 122 NET_LOG_EVENT("UpdateManagerProperties", ""); 123 shill_manager_->GetProperties( 124 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback, 125 AsWeakPtr())); 126} 127 128bool ShillPropertyHandler::IsTechnologyAvailable( 129 const std::string& technology) const { 130 return available_technologies_.count(technology) != 0; 131} 132 133bool ShillPropertyHandler::IsTechnologyEnabled( 134 const std::string& technology) const { 135 return enabled_technologies_.count(technology) != 0; 136} 137 138bool ShillPropertyHandler::IsTechnologyEnabling( 139 const std::string& technology) const { 140 return enabling_technologies_.count(technology) != 0; 141} 142 143bool ShillPropertyHandler::IsTechnologyUninitialized( 144 const std::string& technology) const { 145 return uninitialized_technologies_.count(technology) != 0; 146} 147 148void ShillPropertyHandler::SetTechnologyEnabled( 149 const std::string& technology, 150 bool enabled, 151 const network_handler::ErrorCallback& error_callback) { 152 if (enabled) { 153 enabling_technologies_.insert(technology); 154 shill_manager_->EnableTechnology( 155 technology, 156 base::Bind(&base::DoNothing), 157 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed, 158 AsWeakPtr(), technology, error_callback)); 159 } else { 160 // Immediately clear locally from enabled and enabling lists. 161 enabled_technologies_.erase(technology); 162 enabling_technologies_.erase(technology); 163 shill_manager_->DisableTechnology( 164 technology, 165 base::Bind(&base::DoNothing), 166 base::Bind(&network_handler::ShillErrorCallbackFunction, 167 "SetTechnologyEnabled Failed", 168 technology, error_callback)); 169 } 170} 171 172void ShillPropertyHandler::SetCheckPortalList( 173 const std::string& check_portal_list) { 174 base::StringValue value(check_portal_list); 175 shill_manager_->SetProperty( 176 shill::kCheckPortalListProperty, 177 value, 178 base::Bind(&base::DoNothing), 179 base::Bind(&network_handler::ShillErrorCallbackFunction, 180 "SetCheckPortalList Failed", 181 "", network_handler::ErrorCallback())); 182} 183 184void ShillPropertyHandler::RequestScan() const { 185 shill_manager_->RequestScan( 186 "", 187 base::Bind(&base::DoNothing), 188 base::Bind(&network_handler::ShillErrorCallbackFunction, 189 "RequestScan Failed", 190 "", network_handler::ErrorCallback())); 191} 192 193void ShillPropertyHandler::ConnectToBestServices() const { 194 NET_LOG_EVENT("ConnectToBestServices", ""); 195 shill_manager_->ConnectToBestServices( 196 base::Bind(&base::DoNothing), 197 base::Bind(&network_handler::ShillErrorCallbackFunction, 198 "ConnectToBestServices Failed", 199 "", network_handler::ErrorCallback())); 200} 201 202void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type, 203 const std::string& path) { 204 if (pending_updates_[type].find(path) != pending_updates_[type].end()) 205 return; // Update already requested. 206 207 NET_LOG_DEBUG("Request Properties", path); 208 pending_updates_[type].insert(path); 209 if (type == ManagedState::MANAGED_TYPE_NETWORK || 210 type == ManagedState::MANAGED_TYPE_FAVORITE) { 211 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 212 dbus::ObjectPath(path), 213 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 214 AsWeakPtr(), type, path)); 215 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 216 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( 217 dbus::ObjectPath(path), 218 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 219 AsWeakPtr(), type, path)); 220 } else { 221 NOTREACHED(); 222 } 223} 224 225void ShillPropertyHandler::OnPropertyChanged(const std::string& key, 226 const base::Value& value) { 227 ManagerPropertyChanged(key, value); 228 CheckPendingStateListUpdates(key); 229} 230 231//------------------------------------------------------------------------------ 232// Private methods 233 234void ShillPropertyHandler::ManagerPropertiesCallback( 235 DBusMethodCallStatus call_status, 236 const base::DictionaryValue& properties) { 237 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 238 NET_LOG_ERROR("ManagerPropertiesCallback", 239 base::StringPrintf("Failed: %d", call_status)); 240 return; 241 } 242 NET_LOG_EVENT("ManagerPropertiesCallback", "Success"); 243 const base::Value* update_service_value = NULL; 244 const base::Value* update_service_complete_value = NULL; 245 for (base::DictionaryValue::Iterator iter(properties); 246 !iter.IsAtEnd(); iter.Advance()) { 247 // Defer updating Services until all other properties have been updated. 248 if (iter.key() == shill::kServicesProperty) 249 update_service_value = &iter.value(); 250 else if (iter.key() == shill::kServiceCompleteListProperty) 251 update_service_complete_value = &iter.value(); 252 else 253 ManagerPropertyChanged(iter.key(), iter.value()); 254 } 255 // Update Services which can safely assume other properties have been set. 256 if (update_service_value) 257 ManagerPropertyChanged(shill::kServicesProperty, *update_service_value); 258 // Update ServiceCompleteList which skips entries that have already been 259 // requested for Services. 260 if (update_service_complete_value) { 261 ManagerPropertyChanged(shill::kServiceCompleteListProperty, 262 *update_service_complete_value); 263 } 264 265 CheckPendingStateListUpdates(""); 266} 267 268void 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::kServicesProperty) && 272 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) { 273 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 274 } 275 // Both Network update requests and Favorite update requests will affect 276 // the list of favorites, so wait for both to complete. 277 if ((key.empty() || key == shill::kServiceCompleteListProperty) && 278 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0 && 279 pending_updates_[ManagedState::MANAGED_TYPE_FAVORITE].size() == 0) { 280 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE); 281 } 282 if ((key.empty() || key == shill::kDevicesProperty) && 283 pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) { 284 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 285 } 286} 287 288void ShillPropertyHandler::ManagerPropertyChanged(const std::string& key, 289 const base::Value& value) { 290 if (key == shill::kDefaultServiceProperty) { 291 std::string service_path; 292 value.GetAsString(&service_path); 293 listener_->DefaultNetworkServiceChanged(service_path); 294 } else if (key == shill::kServicesProperty) { 295 const base::ListValue* vlist = GetListValue(key, value); 296 if (vlist) { 297 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 298 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 299 // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK, 300 // however that prevents us from receiving Strength updates from inactive 301 // networks. The overhead for observing all services is not unreasonable 302 // (and we limit the max number of observed services to kMaxObserved). 303 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 304 } 305 } else if (key == shill::kServiceCompleteListProperty) { 306 const base::ListValue* vlist = GetListValue(key, value); 307 if (vlist) { 308 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_FAVORITE, *vlist); 309 UpdateProperties(ManagedState::MANAGED_TYPE_FAVORITE, *vlist); 310 } 311 } else if (key == shill::kDevicesProperty) { 312 const base::ListValue* vlist = GetListValue(key, value); 313 if (vlist) { 314 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 315 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 316 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 317 } 318 } else if (key == shill::kAvailableTechnologiesProperty) { 319 const base::ListValue* vlist = GetListValue(key, value); 320 if (vlist) 321 UpdateAvailableTechnologies(*vlist); 322 } else if (key == shill::kEnabledTechnologiesProperty) { 323 const base::ListValue* vlist = GetListValue(key, value); 324 if (vlist) 325 UpdateEnabledTechnologies(*vlist); 326 } else if (key == shill::kUninitializedTechnologiesProperty) { 327 const base::ListValue* vlist = GetListValue(key, value); 328 if (vlist) 329 UpdateUninitializedTechnologies(*vlist); 330 } else if (key == shill::kProfilesProperty) { 331 listener_->ProfileListChanged(); 332 } else if (key == shill::kCheckPortalListProperty) { 333 std::string check_portal_list; 334 if (value.GetAsString(&check_portal_list)) 335 listener_->CheckPortalListChanged(check_portal_list); 336 } else { 337 VLOG(2) << "Ignored Manager Property: " << key; 338 } 339} 340 341void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type, 342 const base::ListValue& entries) { 343 std::set<std::string>& requested_updates = requested_updates_[type]; 344 std::set<std::string>& requested_service_updates = 345 requested_updates_[ManagedState::MANAGED_TYPE_NETWORK]; // For favorites 346 std::set<std::string> new_requested_updates; 347 NET_LOG_DEBUG( 348 base::StringPrintf("UpdateProperties: %" PRIuS, entries.GetSize()), 349 ManagedState::TypeToString(type)); 350 for (base::ListValue::const_iterator iter = entries.begin(); 351 iter != entries.end(); ++iter) { 352 std::string path; 353 (*iter)->GetAsString(&path); 354 if (path.empty()) 355 continue; 356 if (type == ManagedState::MANAGED_TYPE_FAVORITE && 357 requested_service_updates.count(path) > 0) 358 continue; // Update already requested 359 360 // We add a special case for devices here to work around an issue in shill 361 // that prevents it from sending property changed signals for cellular 362 // devices (see crbug.com/321854). 363 if (type == ManagedState::MANAGED_TYPE_DEVICE || 364 requested_updates.find(path) == requested_updates.end()) 365 RequestProperties(type, path); 366 new_requested_updates.insert(path); 367 } 368 requested_updates.swap(new_requested_updates); 369} 370 371void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type, 372 const base::ListValue& entries) { 373 DCHECK(type == ManagedState::MANAGED_TYPE_NETWORK || 374 type == ManagedState::MANAGED_TYPE_DEVICE); 375 ShillPropertyObserverMap& observer_map = 376 (type == ManagedState::MANAGED_TYPE_NETWORK) 377 ? observed_networks_ : observed_devices_; 378 ShillPropertyObserverMap new_observed; 379 for (base::ListValue::const_iterator iter1 = entries.begin(); 380 iter1 != entries.end(); ++iter1) { 381 std::string path; 382 (*iter1)->GetAsString(&path); 383 if (path.empty()) 384 continue; 385 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path); 386 if (iter2 != observer_map.end()) { 387 new_observed[path] = iter2->second; 388 } else { 389 // Create an observer for future updates. 390 new_observed[path] = new ShillPropertyObserver( 391 type, path, base::Bind( 392 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr())); 393 } 394 observer_map.erase(path); 395 // Limit the number of observed services. 396 if (new_observed.size() >= kMaxObserved) 397 break; 398 } 399 // Delete network service observers still in observer_map. 400 for (ShillPropertyObserverMap::iterator iter = observer_map.begin(); 401 iter != observer_map.end(); ++iter) { 402 delete iter->second; 403 } 404 observer_map.swap(new_observed); 405} 406 407void ShillPropertyHandler::UpdateAvailableTechnologies( 408 const base::ListValue& technologies) { 409 available_technologies_.clear(); 410 NET_LOG_EVENT("AvailableTechnologiesChanged", 411 base::StringPrintf("Size: %" PRIuS, technologies.GetSize())); 412 for (base::ListValue::const_iterator iter = technologies.begin(); 413 iter != technologies.end(); ++iter) { 414 std::string technology; 415 (*iter)->GetAsString(&technology); 416 DCHECK(!technology.empty()); 417 available_technologies_.insert(technology); 418 } 419 listener_->TechnologyListChanged(); 420} 421 422void ShillPropertyHandler::UpdateEnabledTechnologies( 423 const base::ListValue& technologies) { 424 enabled_technologies_.clear(); 425 NET_LOG_EVENT("EnabledTechnologiesChanged", 426 base::StringPrintf("Size: %" PRIuS, technologies.GetSize())); 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 enabled_technologies_.insert(technology); 433 enabling_technologies_.erase(technology); 434 } 435 listener_->TechnologyListChanged(); 436} 437 438void ShillPropertyHandler::UpdateUninitializedTechnologies( 439 const base::ListValue& technologies) { 440 uninitialized_technologies_.clear(); 441 NET_LOG_EVENT("UninitializedTechnologiesChanged", 442 base::StringPrintf("Size: %" PRIuS, technologies.GetSize())); 443 for (base::ListValue::const_iterator iter = technologies.begin(); 444 iter != technologies.end(); ++iter) { 445 std::string technology; 446 (*iter)->GetAsString(&technology); 447 DCHECK(!technology.empty()); 448 uninitialized_technologies_.insert(technology); 449 } 450 listener_->TechnologyListChanged(); 451} 452 453void ShillPropertyHandler::EnableTechnologyFailed( 454 const std::string& technology, 455 const network_handler::ErrorCallback& error_callback, 456 const std::string& dbus_error_name, 457 const std::string& dbus_error_message) { 458 enabling_technologies_.erase(technology); 459 network_handler::ShillErrorCallbackFunction( 460 "EnableTechnology Failed", 461 technology, error_callback, 462 dbus_error_name, dbus_error_message); 463} 464 465void ShillPropertyHandler::GetPropertiesCallback( 466 ManagedState::ManagedType type, 467 const std::string& path, 468 DBusMethodCallStatus call_status, 469 const base::DictionaryValue& properties) { 470 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type), 471 path); 472 pending_updates_[type].erase(path); 473 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 474 // The shill service no longer exists. This can happen when a network 475 // has been removed. 476 NET_LOG_DEBUG("Failed to get properties", 477 base::StringPrintf("%s: %d", path.c_str(), call_status)); 478 return; 479 } 480 // Update Favorite properties for networks in the Services list. 481 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 482 // Only networks with a ProfilePath set are Favorites. 483 std::string profile_path; 484 properties.GetStringWithoutPathExpansion( 485 shill::kProfileProperty, &profile_path); 486 if (!profile_path.empty()) { 487 listener_->UpdateManagedStateProperties( 488 ManagedState::MANAGED_TYPE_FAVORITE, path, properties); 489 } 490 } 491 listener_->UpdateManagedStateProperties(type, path, properties); 492 493 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 494 // Request IPConfig properties. 495 const base::Value* value; 496 if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value)) 497 RequestIPConfig(type, path, *value); 498 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 499 // Clear and request IPConfig properties for each entry in IPConfigs. 500 const base::Value* value; 501 if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value)) 502 RequestIPConfigsList(type, path, *value); 503 } 504 505 // Notify the listener only when all updates for that type have completed. 506 if (pending_updates_[type].size() == 0) { 507 listener_->ManagedStateListChanged(type); 508 // Notify that Favorites have changed when notifying for Networks if there 509 // are no additional Favorite updates pending. 510 if (type == ManagedState::MANAGED_TYPE_NETWORK && 511 pending_updates_[ManagedState::MANAGED_TYPE_FAVORITE].size() == 0) { 512 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE); 513 } 514 } 515} 516 517void ShillPropertyHandler::PropertyChangedCallback( 518 ManagedState::ManagedType type, 519 const std::string& path, 520 const std::string& key, 521 const base::Value& value) { 522 if (type == ManagedState::MANAGED_TYPE_NETWORK && 523 key == shill::kIPConfigProperty) { 524 RequestIPConfig(type, path, value); 525 } else if (type == ManagedState::MANAGED_TYPE_DEVICE && 526 key == shill::kIPConfigsProperty) { 527 RequestIPConfigsList(type, path, value); 528 } 529 530 if (type == ManagedState::MANAGED_TYPE_NETWORK) 531 listener_->UpdateNetworkServiceProperty(path, key, value); 532 else if (type == ManagedState::MANAGED_TYPE_DEVICE) 533 listener_->UpdateDeviceProperty(path, key, value); 534 else 535 NOTREACHED(); 536} 537 538void ShillPropertyHandler::RequestIPConfig( 539 ManagedState::ManagedType type, 540 const std::string& path, 541 const base::Value& ip_config_path_value) { 542 std::string ip_config_path; 543 if (!ip_config_path_value.GetAsString(&ip_config_path) || 544 ip_config_path.empty()) { 545 NET_LOG_ERROR("Invalid IPConfig", path); 546 return; 547 } 548 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 549 dbus::ObjectPath(ip_config_path), 550 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 551 AsWeakPtr(), type, path, ip_config_path)); 552} 553 554void ShillPropertyHandler::RequestIPConfigsList( 555 ManagedState::ManagedType type, 556 const std::string& path, 557 const base::Value& ip_config_list_value) { 558 const base::ListValue* ip_configs; 559 if (!ip_config_list_value.GetAsList(&ip_configs)) 560 return; 561 for (base::ListValue::const_iterator iter = ip_configs->begin(); 562 iter != ip_configs->end(); ++iter) { 563 RequestIPConfig(type, path, **iter); 564 } 565} 566 567void ShillPropertyHandler::GetIPConfigCallback( 568 ManagedState::ManagedType type, 569 const std::string& path, 570 const std::string& ip_config_path, 571 DBusMethodCallStatus call_status, 572 const base::DictionaryValue& properties) { 573 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 574 NET_LOG_ERROR("Failed to get IP Config properties", 575 base::StringPrintf("%s: %d", path.c_str(), call_status)); 576 return; 577 } 578 NET_LOG_EVENT("IP Config properties received", path); 579 listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties); 580} 581 582} // namespace internal 583} // namespace chromeos 584