shill_property_handler.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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::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 312void 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 337void 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 371void 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 387void 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 404void 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 420void 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 432void 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 466void 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 487void 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 503void 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 516void 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