shill_property_handler.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 const base::Value* update_service_value = NULL; 248 const base::Value* update_service_complete_value = NULL; 249 for (base::DictionaryValue::Iterator iter(properties); 250 !iter.IsAtEnd(); iter.Advance()) { 251 // Defer updating Services until all other properties have been updated. 252 if (iter.key() == shill::kServicesProperty) 253 update_service_value = &iter.value(); 254 else if (iter.key() == shill::kServiceCompleteListProperty) 255 update_service_complete_value = &iter.value(); 256 else 257 ManagerPropertyChanged(iter.key(), iter.value()); 258 } 259 // Update Service lists after other Manager properties. Update 260 // ServiceCompleteList first so that Services (visible) entries already exist. 261 if (update_service_complete_value) { 262 ManagerPropertyChanged(shill::kServiceCompleteListProperty, 263 *update_service_complete_value); 264 } 265 if (update_service_value) 266 ManagerPropertyChanged(shill::kServicesProperty, *update_service_value); 267 268 CheckPendingStateListUpdates(""); 269} 270 271void ShillPropertyHandler::CheckPendingStateListUpdates( 272 const std::string& key) { 273 // Once there are no pending updates, signal the state list changed callbacks. 274 if ((key.empty() || key == shill::kServiceCompleteListProperty) && 275 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) { 276 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 277 } 278 if ((key.empty() || key == shill::kDevicesProperty) && 279 pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) { 280 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 281 } 282 // For emitted Services updates only, we also need to signal that the list 283 // changed in case the visibility changed. 284 if (key == shill::kServicesProperty && 285 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) { 286 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 287 } 288} 289 290void ShillPropertyHandler::ManagerPropertyChanged(const std::string& key, 291 const base::Value& value) { 292 NET_LOG_DEBUG("ManagerPropertyChanged", key); 293 if (key == shill::kDefaultServiceProperty) { 294 std::string service_path; 295 value.GetAsString(&service_path); 296 listener_->DefaultNetworkServiceChanged(service_path); 297 } else if (key == shill::kServicesProperty) { 298 const base::ListValue* vlist = GetListValue(key, value); 299 if (vlist) { 300 // Update the visibility of networks in ServiceCompleteList. Note that 301 // this relies on Shill emitting changes to ServiceCompleteList before 302 // changes to Services. TODO(stevenjb): Eliminate this and rely on 303 // Service.Visible instead. 304 listener_->UpdateVisibleNetworks(*vlist); 305 306 // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK, 307 // however that prevents us from receiving Strength updates from inactive 308 // networks. The overhead for observing all services is not unreasonable 309 // (and we limit the max number of observed services to kMaxObserved). 310 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 311 } 312 } else if (key == shill::kServiceCompleteListProperty) { 313 const base::ListValue* vlist = GetListValue(key, value); 314 if (vlist) { 315 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 316 UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 317 } 318 } else if (key == shill::kServiceWatchListProperty) { 319 // Currently we ignore the watch list. 320 } else if (key == shill::kDevicesProperty) { 321 const base::ListValue* vlist = GetListValue(key, value); 322 if (vlist) { 323 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 324 UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 325 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 326 } 327 } else if (key == shill::kAvailableTechnologiesProperty) { 328 const base::ListValue* vlist = GetListValue(key, value); 329 if (vlist) 330 UpdateAvailableTechnologies(*vlist); 331 } else if (key == shill::kEnabledTechnologiesProperty) { 332 const base::ListValue* vlist = GetListValue(key, value); 333 if (vlist) 334 UpdateEnabledTechnologies(*vlist); 335 } else if (key == shill::kUninitializedTechnologiesProperty) { 336 const base::ListValue* vlist = GetListValue(key, value); 337 if (vlist) 338 UpdateUninitializedTechnologies(*vlist); 339 } else if (key == shill::kProfilesProperty) { 340 listener_->ProfileListChanged(); 341 } else if (key == shill::kCheckPortalListProperty) { 342 std::string check_portal_list; 343 if (value.GetAsString(&check_portal_list)) 344 listener_->CheckPortalListChanged(check_portal_list); 345 } else { 346 VLOG(2) << "Ignored Manager Property: " << key; 347 } 348} 349 350void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type, 351 const base::ListValue& entries) { 352 std::set<std::string>& requested_updates = requested_updates_[type]; 353 std::set<std::string> new_requested_updates; 354 NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type), 355 base::StringPrintf("%" PRIuS, entries.GetSize())); 356 for (base::ListValue::const_iterator iter = entries.begin(); 357 iter != entries.end(); ++iter) { 358 std::string path; 359 (*iter)->GetAsString(&path); 360 if (path.empty()) 361 continue; 362 363 // We add a special case for devices here to work around an issue in shill 364 // that prevents it from sending property changed signals for cellular 365 // devices (see crbug.com/321854). 366 if (type == ManagedState::MANAGED_TYPE_DEVICE || 367 requested_updates.find(path) == requested_updates.end()) { 368 RequestProperties(type, path); 369 } 370 new_requested_updates.insert(path); 371 } 372 requested_updates.swap(new_requested_updates); 373} 374 375void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type, 376 const base::ListValue& entries) { 377 ShillPropertyObserverMap& observer_map = 378 (type == ManagedState::MANAGED_TYPE_NETWORK) 379 ? observed_networks_ : observed_devices_; 380 ShillPropertyObserverMap new_observed; 381 for (base::ListValue::const_iterator iter1 = entries.begin(); 382 iter1 != entries.end(); ++iter1) { 383 std::string path; 384 (*iter1)->GetAsString(&path); 385 if (path.empty()) 386 continue; 387 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path); 388 if (iter2 != observer_map.end()) { 389 new_observed[path] = iter2->second; 390 } else { 391 // Create an observer for future updates. 392 new_observed[path] = new ShillPropertyObserver( 393 type, path, base::Bind( 394 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr())); 395 } 396 observer_map.erase(path); 397 // Limit the number of observed services. 398 if (new_observed.size() >= kMaxObserved) 399 break; 400 } 401 // Delete network service observers still in observer_map. 402 for (ShillPropertyObserverMap::iterator iter = observer_map.begin(); 403 iter != observer_map.end(); ++iter) { 404 delete iter->second; 405 } 406 observer_map.swap(new_observed); 407} 408 409void ShillPropertyHandler::UpdateAvailableTechnologies( 410 const base::ListValue& technologies) { 411 std::stringstream technologies_str; 412 technologies_str << technologies; 413 NET_LOG_EVENT("AvailableTechnologies:", technologies_str.str()); 414 available_technologies_.clear(); 415 for (base::ListValue::const_iterator iter = technologies.begin(); 416 iter != technologies.end(); ++iter) { 417 std::string technology; 418 (*iter)->GetAsString(&technology); 419 DCHECK(!technology.empty()); 420 available_technologies_.insert(technology); 421 } 422 listener_->TechnologyListChanged(); 423} 424 425void ShillPropertyHandler::UpdateEnabledTechnologies( 426 const base::ListValue& technologies) { 427 std::stringstream technologies_str; 428 technologies_str << technologies; 429 NET_LOG_EVENT("EnabledTechnologies:", technologies_str.str()); 430 enabled_technologies_.clear(); 431 for (base::ListValue::const_iterator iter = technologies.begin(); 432 iter != technologies.end(); ++iter) { 433 std::string technology; 434 (*iter)->GetAsString(&technology); 435 DCHECK(!technology.empty()); 436 enabled_technologies_.insert(technology); 437 enabling_technologies_.erase(technology); 438 } 439 listener_->TechnologyListChanged(); 440} 441 442void ShillPropertyHandler::UpdateUninitializedTechnologies( 443 const base::ListValue& technologies) { 444 std::stringstream technologies_str; 445 technologies_str << technologies; 446 NET_LOG_EVENT("UninitializedTechnologies:", technologies_str.str()); 447 uninitialized_technologies_.clear(); 448 for (base::ListValue::const_iterator iter = technologies.begin(); 449 iter != technologies.end(); ++iter) { 450 std::string technology; 451 (*iter)->GetAsString(&technology); 452 DCHECK(!technology.empty()); 453 uninitialized_technologies_.insert(technology); 454 } 455 listener_->TechnologyListChanged(); 456} 457 458void ShillPropertyHandler::EnableTechnologyFailed( 459 const std::string& technology, 460 const network_handler::ErrorCallback& error_callback, 461 const std::string& dbus_error_name, 462 const std::string& dbus_error_message) { 463 enabling_technologies_.erase(technology); 464 network_handler::ShillErrorCallbackFunction( 465 "EnableTechnology Failed", 466 technology, error_callback, 467 dbus_error_name, dbus_error_message); 468} 469 470void ShillPropertyHandler::GetPropertiesCallback( 471 ManagedState::ManagedType type, 472 const std::string& path, 473 DBusMethodCallStatus call_status, 474 const base::DictionaryValue& properties) { 475 NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type), 476 path); 477 pending_updates_[type].erase(path); 478 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 479 // The shill service no longer exists. This can happen when a network 480 // has been removed. 481 NET_LOG_DEBUG("Failed to get properties", 482 base::StringPrintf("%s: %d", path.c_str(), call_status)); 483 return; 484 } 485 listener_->UpdateManagedStateProperties(type, path, properties); 486 487 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 488 // Request IPConfig properties. 489 const base::Value* value; 490 if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value)) 491 RequestIPConfig(type, path, *value); 492 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 493 // Clear and request IPConfig properties for each entry in IPConfigs. 494 const base::Value* value; 495 if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value)) 496 RequestIPConfigsList(type, path, *value); 497 } 498 499 // Notify the listener only when all updates for that type have completed. 500 if (pending_updates_[type].size() == 0) 501 listener_->ManagedStateListChanged(type); 502} 503 504void ShillPropertyHandler::PropertyChangedCallback( 505 ManagedState::ManagedType type, 506 const std::string& path, 507 const std::string& key, 508 const base::Value& value) { 509 if (type == ManagedState::MANAGED_TYPE_NETWORK && 510 key == shill::kIPConfigProperty) { 511 RequestIPConfig(type, path, value); 512 } else if (type == ManagedState::MANAGED_TYPE_DEVICE && 513 key == shill::kIPConfigsProperty) { 514 RequestIPConfigsList(type, path, value); 515 } 516 517 if (type == ManagedState::MANAGED_TYPE_NETWORK) 518 listener_->UpdateNetworkServiceProperty(path, key, value); 519 else if (type == ManagedState::MANAGED_TYPE_DEVICE) 520 listener_->UpdateDeviceProperty(path, key, value); 521 else 522 NOTREACHED(); 523} 524 525void ShillPropertyHandler::RequestIPConfig( 526 ManagedState::ManagedType type, 527 const std::string& path, 528 const base::Value& ip_config_path_value) { 529 std::string ip_config_path; 530 if (!ip_config_path_value.GetAsString(&ip_config_path) || 531 ip_config_path.empty()) { 532 NET_LOG_ERROR("Invalid IPConfig", path); 533 return; 534 } 535 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 536 dbus::ObjectPath(ip_config_path), 537 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 538 AsWeakPtr(), type, path, ip_config_path)); 539} 540 541void ShillPropertyHandler::RequestIPConfigsList( 542 ManagedState::ManagedType type, 543 const std::string& path, 544 const base::Value& ip_config_list_value) { 545 const base::ListValue* ip_configs; 546 if (!ip_config_list_value.GetAsList(&ip_configs)) 547 return; 548 for (base::ListValue::const_iterator iter = ip_configs->begin(); 549 iter != ip_configs->end(); ++iter) { 550 RequestIPConfig(type, path, **iter); 551 } 552} 553 554void ShillPropertyHandler::GetIPConfigCallback( 555 ManagedState::ManagedType type, 556 const std::string& path, 557 const std::string& ip_config_path, 558 DBusMethodCallStatus call_status, 559 const base::DictionaryValue& properties) { 560 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 561 NET_LOG_ERROR("Failed to get IP Config properties", 562 base::StringPrintf("%s: %d", path.c_str(), call_status)); 563 return; 564 } 565 NET_LOG_EVENT("IP Config properties received", path); 566 listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties); 567} 568 569} // namespace internal 570} // namespace chromeos 571