shill_property_handler.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_service_client.h" 18#include "chromeos/network/network_event_log.h" 19#include "chromeos/network/network_state.h" 20#include "dbus/object_path.h" 21#include "third_party/cros_system_api/dbus/service_constants.h" 22 23namespace { 24 25// Limit the number of services or devices we observe. Since they are listed in 26// priority order, it should be reasonable to ignore services past this. 27const size_t kMaxObserved = 100; 28 29const base::ListValue* GetListValue(const std::string& key, 30 const base::Value& value) { 31 const base::ListValue* vlist = NULL; 32 if (!value.GetAsList(&vlist)) { 33 LOG(ERROR) << "Error parsing key as list: " << key; 34 return NULL; 35 } 36 return vlist; 37} 38 39} // namespace 40 41namespace chromeos { 42namespace internal { 43 44// Class to manage Shill service property changed observers. Observers are 45// added on construction and removed on destruction. Runs the handler when 46// OnPropertyChanged is called. 47class ShillPropertyObserver : public ShillPropertyChangedObserver { 48 public: 49 typedef base::Callback<void(ManagedState::ManagedType type, 50 const std::string& service, 51 const std::string& name, 52 const base::Value& value)> Handler; 53 54 ShillPropertyObserver(ManagedState::ManagedType type, 55 const std::string& path, 56 const Handler& handler) 57 : type_(type), 58 path_(path), 59 handler_(handler) { 60 if (type_ == ManagedState::MANAGED_TYPE_NETWORK) { 61 DBusThreadManager::Get()->GetShillServiceClient()-> 62 AddPropertyChangedObserver(dbus::ObjectPath(path_), this); 63 } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) { 64 DBusThreadManager::Get()->GetShillDeviceClient()-> 65 AddPropertyChangedObserver(dbus::ObjectPath(path_), this); 66 } else { 67 NOTREACHED(); 68 } 69 } 70 71 virtual ~ShillPropertyObserver() { 72 if (type_ == ManagedState::MANAGED_TYPE_NETWORK) { 73 DBusThreadManager::Get()->GetShillServiceClient()-> 74 RemovePropertyChangedObserver(dbus::ObjectPath(path_), this); 75 } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) { 76 DBusThreadManager::Get()->GetShillDeviceClient()-> 77 RemovePropertyChangedObserver(dbus::ObjectPath(path_), this); 78 } else { 79 NOTREACHED(); 80 } 81 } 82 83 // ShillPropertyChangedObserver overrides. 84 virtual void OnPropertyChanged(const std::string& key, 85 const base::Value& value) OVERRIDE { 86 handler_.Run(type_, path_, key, value); 87 } 88 89 private: 90 ManagedState::ManagedType type_; 91 std::string path_; 92 Handler handler_; 93 94 DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver); 95}; 96 97//------------------------------------------------------------------------------ 98// ShillPropertyHandler 99 100ShillPropertyHandler::ShillPropertyHandler(Listener* listener) 101 : listener_(listener), 102 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) { 103} 104 105ShillPropertyHandler::~ShillPropertyHandler() { 106 // Delete network service observers. 107 STLDeleteContainerPairSecondPointers( 108 observed_networks_.begin(), observed_networks_.end()); 109 STLDeleteContainerPairSecondPointers( 110 observed_devices_.begin(), observed_devices_.end()); 111 CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient()); 112 shill_manager_->RemovePropertyChangedObserver(this); 113} 114 115void ShillPropertyHandler::Init() { 116 shill_manager_->GetProperties( 117 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback, 118 AsWeakPtr())); 119 shill_manager_->AddPropertyChangedObserver(this); 120} 121 122bool ShillPropertyHandler::IsTechnologyAvailable( 123 const std::string& technology) const { 124 return available_technologies_.count(technology) != 0; 125} 126 127bool ShillPropertyHandler::IsTechnologyEnabled( 128 const std::string& technology) const { 129 return enabled_technologies_.count(technology) != 0; 130} 131 132bool ShillPropertyHandler::IsTechnologyEnabling( 133 const std::string& technology) const { 134 return enabling_technologies_.count(technology) != 0; 135} 136 137bool ShillPropertyHandler::IsTechnologyUninitialized( 138 const std::string& technology) const { 139 return uninitialized_technologies_.count(technology) != 0; 140} 141 142void ShillPropertyHandler::SetTechnologyEnabled( 143 const std::string& technology, 144 bool enabled, 145 const network_handler::ErrorCallback& error_callback) { 146 if (enabled) { 147 enabling_technologies_.insert(technology); 148 shill_manager_->EnableTechnology( 149 technology, 150 base::Bind(&base::DoNothing), 151 base::Bind(&ShillPropertyHandler::EnableTechnologyFailed, 152 AsWeakPtr(), technology, error_callback)); 153 } else { 154 // Imediately clear locally from enabled and enabling lists. 155 enabled_technologies_.erase(technology); 156 enabling_technologies_.erase(technology); 157 shill_manager_->DisableTechnology( 158 technology, 159 base::Bind(&base::DoNothing), 160 base::Bind(&network_handler::ShillErrorCallbackFunction, 161 technology, error_callback)); 162 } 163} 164 165void ShillPropertyHandler::SetCheckPortalList( 166 const std::string& check_portal_list) { 167 base::StringValue value(check_portal_list); 168 shill_manager_->SetProperty( 169 flimflam::kCheckPortalListProperty, 170 value, 171 base::Bind(&base::DoNothing), 172 base::Bind(&network_handler::ShillErrorCallbackFunction, 173 "", network_handler::ErrorCallback())); 174} 175 176void ShillPropertyHandler::RequestScan() const { 177 shill_manager_->RequestScan( 178 "", 179 base::Bind(&base::DoNothing), 180 base::Bind(&network_handler::ShillErrorCallbackFunction, 181 "", network_handler::ErrorCallback())); 182} 183 184void ShillPropertyHandler::ConnectToBestServices() const { 185 NET_LOG_EVENT("ConnectToBestServices", ""); 186 shill_manager_->ConnectToBestServices( 187 base::Bind(&base::DoNothing), 188 base::Bind(&network_handler::ShillErrorCallbackFunction, 189 "", network_handler::ErrorCallback())); 190} 191 192void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type, 193 const std::string& path) { 194 if (pending_updates_[type].find(path) != pending_updates_[type].end()) 195 return; // Update already requested. 196 197 pending_updates_[type].insert(path); 198 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 199 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 200 dbus::ObjectPath(path), 201 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 202 AsWeakPtr(), type, path)); 203 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 204 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( 205 dbus::ObjectPath(path), 206 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 207 AsWeakPtr(), type, path)); 208 } else { 209 NOTREACHED(); 210 } 211} 212 213void ShillPropertyHandler::OnPropertyChanged(const std::string& key, 214 const base::Value& value) { 215 if (ManagerPropertyChanged(key, value)) { 216 std::string detail = key; 217 detail += " = " + network_event_log::ValueAsString(value); 218 NET_LOG_DEBUG("ManagerPropertyChanged", detail); 219 listener_->NotifyManagerPropertyChanged(); 220 } 221 // If the service or device list changed and there are no pending 222 // updates, signal the state list changed callback. 223 if ((key == flimflam::kServicesProperty) && 224 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) { 225 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 226 } 227 if (key == flimflam::kDevicesProperty && 228 pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) { 229 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 230 } 231} 232 233//------------------------------------------------------------------------------ 234// Private methods 235 236void ShillPropertyHandler::ManagerPropertiesCallback( 237 DBusMethodCallStatus call_status, 238 const base::DictionaryValue& properties) { 239 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 240 NET_LOG_ERROR("ManagerPropertiesCallback", 241 base::StringPrintf("Failed: %d", call_status)); 242 return; 243 } 244 NET_LOG_EVENT("ManagerPropertiesCallback", "Success"); 245 bool notify = false; 246 bool update_service_list = false; 247 for (base::DictionaryValue::Iterator iter(properties); 248 !iter.IsAtEnd(); iter.Advance()) { 249 // Defer updating Services until all other properties have been updated. 250 if (iter.key() == flimflam::kServicesProperty) 251 update_service_list = true; 252 else 253 notify |= ManagerPropertyChanged(iter.key(), iter.value()); 254 } 255 // Now update the service list which can safely assume other properties have 256 // been initially set. 257 if (update_service_list) { 258 const base::Value* value = NULL; 259 if (properties.GetWithoutPathExpansion(flimflam::kServicesProperty, &value)) 260 notify |= ManagerPropertyChanged(flimflam::kServicesProperty, *value); 261 } 262 if (notify) 263 listener_->NotifyManagerPropertyChanged(); 264 // If there are no pending updates, signal the state list changed callbacks. 265 if (pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) 266 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 267 if (pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) 268 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 269} 270 271bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key, 272 const base::Value& value) { 273 bool notify_manager_changed = false; 274 if (key == flimflam::kServicesProperty) { 275 const base::ListValue* vlist = GetListValue(key, value); 276 if (vlist) { 277 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 278 // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK, 279 // however that prevents us from receiving Strength updates from inactive 280 // networks. The overhead for observing all services is not unreasonable 281 // (and we limit the max number of observed services to kMaxObserved). 282 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 283 } 284 } else if (key == flimflam::kDevicesProperty) { 285 const ListValue* vlist = GetListValue(key, value); 286 if (vlist) { 287 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 288 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 289 } 290 } else if (key == flimflam::kAvailableTechnologiesProperty) { 291 const base::ListValue* vlist = GetListValue(key, value); 292 if (vlist) { 293 UpdateAvailableTechnologies(*vlist); 294 notify_manager_changed = true; 295 } 296 } else if (key == flimflam::kEnabledTechnologiesProperty) { 297 const base::ListValue* vlist = GetListValue(key, value); 298 if (vlist) { 299 UpdateEnabledTechnologies(*vlist); 300 notify_manager_changed = true; 301 } 302 } else if (key == shill::kUninitializedTechnologiesProperty) { 303 const base::ListValue* vlist = GetListValue(key, value); 304 if (vlist) { 305 UpdateUninitializedTechnologies(*vlist); 306 notify_manager_changed = true; 307 } 308 } else if (key == flimflam::kProfilesProperty) { 309 listener_->ProfileListChanged(); 310 } else if (key == flimflam::kCheckPortalListProperty) { 311 std::string check_portal_list; 312 if (value.GetAsString(&check_portal_list)) { 313 listener_->CheckPortalListChanged(check_portal_list); 314 notify_manager_changed = true; 315 } 316 } 317 return notify_manager_changed; 318} 319 320void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type, 321 const base::ListValue& entries) { 322 ShillPropertyObserverMap& observer_map = 323 (type == ManagedState::MANAGED_TYPE_NETWORK) 324 ? observed_networks_ : observed_devices_; 325 ShillPropertyObserverMap new_observed; 326 for (base::ListValue::const_iterator iter1 = entries.begin(); 327 iter1 != entries.end(); ++iter1) { 328 std::string path; 329 (*iter1)->GetAsString(&path); 330 if (path.empty()) 331 continue; 332 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path); 333 if (iter2 != observer_map.end()) { 334 new_observed[path] = iter2->second; 335 } else { 336 // Request an update. 337 RequestProperties(type, path); 338 // Create an observer for future updates. 339 new_observed[path] = new ShillPropertyObserver( 340 type, path, base::Bind( 341 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr())); 342 NET_LOG_DEBUG("StartObserving", path); 343 } 344 observer_map.erase(path); 345 // Limit the number of observed services. 346 if (new_observed.size() >= kMaxObserved) 347 break; 348 } 349 // Delete network service observers still in observer_map. 350 for (ShillPropertyObserverMap::iterator iter = observer_map.begin(); 351 iter != observer_map.end(); ++iter) { 352 NET_LOG_DEBUG("StopObserving", iter->first); 353 delete iter->second; 354 } 355 observer_map.swap(new_observed); 356} 357 358void ShillPropertyHandler::UpdateAvailableTechnologies( 359 const base::ListValue& technologies) { 360 available_technologies_.clear(); 361 NET_LOG_EVENT("AvailableTechnologiesChanged", 362 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 363 for (base::ListValue::const_iterator iter = technologies.begin(); 364 iter != technologies.end(); ++iter) { 365 std::string technology; 366 (*iter)->GetAsString(&technology); 367 DCHECK(!technology.empty()); 368 available_technologies_.insert(technology); 369 } 370} 371 372void ShillPropertyHandler::UpdateEnabledTechnologies( 373 const base::ListValue& technologies) { 374 enabled_technologies_.clear(); 375 NET_LOG_EVENT("EnabledTechnologiesChanged", 376 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 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 enabled_technologies_.insert(technology); 383 enabling_technologies_.erase(technology); 384 } 385} 386 387void ShillPropertyHandler::UpdateUninitializedTechnologies( 388 const base::ListValue& technologies) { 389 uninitialized_technologies_.clear(); 390 NET_LOG_EVENT("UninitializedTechnologiesChanged", 391 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 392 for (base::ListValue::const_iterator iter = technologies.begin(); 393 iter != technologies.end(); ++iter) { 394 std::string technology; 395 (*iter)->GetAsString(&technology); 396 DCHECK(!technology.empty()); 397 uninitialized_technologies_.insert(technology); 398 } 399} 400 401void ShillPropertyHandler::EnableTechnologyFailed( 402 const std::string& technology, 403 const network_handler::ErrorCallback& error_callback, 404 const std::string& error_name, 405 const std::string& error_message) { 406 enabling_technologies_.erase(technology); 407 network_handler::ShillErrorCallbackFunction( 408 technology, error_callback, error_name, error_message); 409} 410 411void ShillPropertyHandler::GetPropertiesCallback( 412 ManagedState::ManagedType type, 413 const std::string& path, 414 DBusMethodCallStatus call_status, 415 const base::DictionaryValue& properties) { 416 VLOG(2) << "GetPropertiesCallback: " << type << " : " << path; 417 pending_updates_[type].erase(path); 418 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 419 LOG(ERROR) << "Failed to get properties for: " << path 420 << ": " << call_status; 421 return; 422 } 423 listener_->UpdateManagedStateProperties(type, path, properties); 424 425 if (properties.HasKey(shill::kIPConfigProperty)) { 426 // Since this is the first time we received properties for this network, 427 // also request its IPConfig parameters. 428 std::string ip_config_path; 429 if (properties.GetString(shill::kIPConfigProperty, &ip_config_path)) { 430 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 431 dbus::ObjectPath(ip_config_path), 432 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 433 AsWeakPtr(), path)); 434 } 435 } 436 437 // Notify the listener only when all updates for that type have completed. 438 if (pending_updates_[type].size() == 0) 439 listener_->ManagedStateListChanged(type); 440} 441 442void ShillPropertyHandler::PropertyChangedCallback( 443 ManagedState::ManagedType type, 444 const std::string& path, 445 const std::string& key, 446 const base::Value& value) { 447 if (type == ManagedState::MANAGED_TYPE_NETWORK) 448 NetworkServicePropertyChangedCallback(path, key, value); 449 else if (type == ManagedState::MANAGED_TYPE_DEVICE) 450 NetworkDevicePropertyChangedCallback(path, key, value); 451 else 452 NOTREACHED(); 453} 454 455void ShillPropertyHandler::NetworkServicePropertyChangedCallback( 456 const std::string& path, 457 const std::string& key, 458 const base::Value& value) { 459 if (key == shill::kIPConfigProperty) { 460 // Request the IPConfig for the network and update network properties 461 // when the request completes. 462 std::string ip_config_path; 463 value.GetAsString(&ip_config_path); 464 DCHECK(!ip_config_path.empty()); 465 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 466 dbus::ObjectPath(ip_config_path), 467 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 468 AsWeakPtr(), path)); 469 } else { 470 listener_->UpdateNetworkServiceProperty(path, key, value); 471 } 472} 473 474void ShillPropertyHandler::GetIPConfigCallback( 475 const std::string& service_path, 476 DBusMethodCallStatus call_status, 477 const base::DictionaryValue& properties) { 478 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 479 LOG(ERROR) << "Failed to get IP Config properties for: " << service_path; 480 return; 481 } 482 const base::Value* ip_address; 483 if (!properties.GetWithoutPathExpansion(flimflam::kAddressProperty, 484 &ip_address)) { 485 LOG(ERROR) << "Failed to get IP Address property for: " << service_path; 486 return; 487 } 488 listener_->UpdateNetworkServiceProperty( 489 service_path, 490 NetworkState::IPConfigProperty(flimflam::kAddressProperty), 491 *ip_address); 492 493 const base::Value* dns_servers = NULL; 494 if (!properties.GetWithoutPathExpansion( 495 flimflam::kNameServersProperty, &dns_servers)) { 496 LOG(ERROR) << "Failed to get Name servers property for: " << service_path; 497 return; 498 } 499 listener_->UpdateNetworkServiceProperty( 500 service_path, 501 NetworkState::IPConfigProperty(flimflam::kNameServersProperty), 502 *dns_servers); 503} 504 505void ShillPropertyHandler::NetworkDevicePropertyChangedCallback( 506 const std::string& path, 507 const std::string& key, 508 const base::Value& value) { 509 listener_->UpdateDeviceProperty(path, key, value); 510} 511 512} // namespace internal 513} // namespace chromeos 514