shill_property_handler.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/string_util.h" 11#include "base/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::RequestScan() const { 166 shill_manager_->RequestScan( 167 "", 168 base::Bind(&base::DoNothing), 169 base::Bind(&network_handler::ShillErrorCallbackFunction, 170 "", network_handler::ErrorCallback())); 171} 172 173void ShillPropertyHandler::ConnectToBestServices() const { 174 NET_LOG_EVENT("ConnectToBestServices", ""); 175 shill_manager_->ConnectToBestServices( 176 base::Bind(&base::DoNothing), 177 base::Bind(&network_handler::ShillErrorCallbackFunction, 178 "", network_handler::ErrorCallback())); 179} 180 181void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type, 182 const std::string& path) { 183 if (pending_updates_[type].find(path) != pending_updates_[type].end()) 184 return; // Update already requested. 185 186 pending_updates_[type].insert(path); 187 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 188 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 189 dbus::ObjectPath(path), 190 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 191 AsWeakPtr(), type, path)); 192 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 193 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( 194 dbus::ObjectPath(path), 195 base::Bind(&ShillPropertyHandler::GetPropertiesCallback, 196 AsWeakPtr(), type, path)); 197 } else { 198 NOTREACHED(); 199 } 200} 201 202void ShillPropertyHandler::OnPropertyChanged(const std::string& key, 203 const base::Value& value) { 204 if (ManagerPropertyChanged(key, value)) { 205 std::string detail = key; 206 detail += " = " + network_event_log::ValueAsString(value); 207 NET_LOG_DEBUG("ManagerPropertyChanged", detail); 208 listener_->NotifyManagerPropertyChanged(); 209 } 210 // If the service or device list changed and there are no pending 211 // updates, signal the state list changed callback. 212 if ((key == flimflam::kServicesProperty) && 213 pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) { 214 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 215 } 216 if (key == flimflam::kDevicesProperty && 217 pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) { 218 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 219 } 220} 221 222//------------------------------------------------------------------------------ 223// Private methods 224 225void ShillPropertyHandler::ManagerPropertiesCallback( 226 DBusMethodCallStatus call_status, 227 const base::DictionaryValue& properties) { 228 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 229 NET_LOG_ERROR("ManagerPropertiesCallback", 230 base::StringPrintf("Failed: %d", call_status)); 231 return; 232 } 233 NET_LOG_EVENT("ManagerPropertiesCallback", "Success"); 234 bool notify = false; 235 bool update_service_list = false; 236 for (base::DictionaryValue::Iterator iter(properties); 237 !iter.IsAtEnd(); iter.Advance()) { 238 // Defer updating Services until all other properties have been updated. 239 if (iter.key() == flimflam::kServicesProperty) 240 update_service_list = true; 241 else 242 notify |= ManagerPropertyChanged(iter.key(), iter.value()); 243 } 244 // Now update the service list which can safely assume other properties have 245 // been initially set. 246 if (update_service_list) { 247 const base::Value* value = NULL; 248 if (properties.GetWithoutPathExpansion(flimflam::kServicesProperty, &value)) 249 notify |= ManagerPropertyChanged(flimflam::kServicesProperty, *value); 250 } 251 if (notify) 252 listener_->NotifyManagerPropertyChanged(); 253 // If there are no pending updates, signal the state list changed callbacks. 254 if (pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) 255 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK); 256 if (pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) 257 listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE); 258} 259 260bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key, 261 const base::Value& value) { 262 bool notify_manager_changed = false; 263 if (key == flimflam::kServicesProperty) { 264 const base::ListValue* vlist = GetListValue(key, value); 265 if (vlist) { 266 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 267 // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK, 268 // however that prevents us from receiving Strength updates from inactive 269 // networks. The overhead for observing all services is not unreasonable 270 // (and we limit the max number of observed services to kMaxObserved). 271 UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist); 272 } 273 } else if (key == flimflam::kDevicesProperty) { 274 const ListValue* vlist = GetListValue(key, value); 275 if (vlist) { 276 listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 277 UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist); 278 } 279 } else if (key == flimflam::kAvailableTechnologiesProperty) { 280 const base::ListValue* vlist = GetListValue(key, value); 281 if (vlist) { 282 UpdateAvailableTechnologies(*vlist); 283 notify_manager_changed = true; 284 } 285 } else if (key == flimflam::kEnabledTechnologiesProperty) { 286 const base::ListValue* vlist = GetListValue(key, value); 287 if (vlist) { 288 UpdateEnabledTechnologies(*vlist); 289 notify_manager_changed = true; 290 } 291 } else if (key == shill::kUninitializedTechnologiesProperty) { 292 const base::ListValue* vlist = GetListValue(key, value); 293 if (vlist) { 294 UpdateUninitializedTechnologies(*vlist); 295 notify_manager_changed = true; 296 } 297 } else if (key == flimflam::kProfilesProperty) { 298 listener_->ProfileListChanged(); 299 } 300 return notify_manager_changed; 301} 302 303void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type, 304 const base::ListValue& entries) { 305 ShillPropertyObserverMap& observer_map = 306 (type == ManagedState::MANAGED_TYPE_NETWORK) 307 ? observed_networks_ : observed_devices_; 308 ShillPropertyObserverMap new_observed; 309 for (base::ListValue::const_iterator iter1 = entries.begin(); 310 iter1 != entries.end(); ++iter1) { 311 std::string path; 312 (*iter1)->GetAsString(&path); 313 if (path.empty()) 314 continue; 315 ShillPropertyObserverMap::iterator iter2 = observer_map.find(path); 316 if (iter2 != observer_map.end()) { 317 new_observed[path] = iter2->second; 318 } else { 319 // Request an update. 320 RequestProperties(type, path); 321 // Create an observer for future updates. 322 new_observed[path] = new ShillPropertyObserver( 323 type, path, base::Bind( 324 &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr())); 325 NET_LOG_DEBUG("StartObserving", path); 326 } 327 observer_map.erase(path); 328 // Limit the number of observed services. 329 if (new_observed.size() >= kMaxObserved) 330 break; 331 } 332 // Delete network service observers still in observer_map. 333 for (ShillPropertyObserverMap::iterator iter = observer_map.begin(); 334 iter != observer_map.end(); ++iter) { 335 NET_LOG_DEBUG("StopObserving", iter->first); 336 delete iter->second; 337 } 338 observer_map.swap(new_observed); 339} 340 341void ShillPropertyHandler::UpdateAvailableTechnologies( 342 const base::ListValue& technologies) { 343 available_technologies_.clear(); 344 NET_LOG_EVENT("AvailableTechnologiesChanged", 345 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 346 for (base::ListValue::const_iterator iter = technologies.begin(); 347 iter != technologies.end(); ++iter) { 348 std::string technology; 349 (*iter)->GetAsString(&technology); 350 DCHECK(!technology.empty()); 351 available_technologies_.insert(technology); 352 } 353} 354 355void ShillPropertyHandler::UpdateEnabledTechnologies( 356 const base::ListValue& technologies) { 357 enabled_technologies_.clear(); 358 NET_LOG_EVENT("EnabledTechnologiesChanged", 359 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 360 for (base::ListValue::const_iterator iter = technologies.begin(); 361 iter != technologies.end(); ++iter) { 362 std::string technology; 363 (*iter)->GetAsString(&technology); 364 DCHECK(!technology.empty()); 365 enabled_technologies_.insert(technology); 366 enabling_technologies_.erase(technology); 367 } 368} 369 370void ShillPropertyHandler::UpdateUninitializedTechnologies( 371 const base::ListValue& technologies) { 372 uninitialized_technologies_.clear(); 373 NET_LOG_EVENT("UninitializedTechnologiesChanged", 374 base::StringPrintf("Size: %"PRIuS, technologies.GetSize())); 375 for (base::ListValue::const_iterator iter = technologies.begin(); 376 iter != technologies.end(); ++iter) { 377 std::string technology; 378 (*iter)->GetAsString(&technology); 379 DCHECK(!technology.empty()); 380 uninitialized_technologies_.insert(technology); 381 } 382} 383 384void ShillPropertyHandler::EnableTechnologyFailed( 385 const std::string& technology, 386 const network_handler::ErrorCallback& error_callback, 387 const std::string& error_name, 388 const std::string& error_message) { 389 enabling_technologies_.erase(technology); 390 network_handler::ShillErrorCallbackFunction( 391 technology, error_callback, error_name, error_message); 392} 393 394void ShillPropertyHandler::GetPropertiesCallback( 395 ManagedState::ManagedType type, 396 const std::string& path, 397 DBusMethodCallStatus call_status, 398 const base::DictionaryValue& properties) { 399 VLOG(2) << "GetPropertiesCallback: " << type << " : " << path; 400 pending_updates_[type].erase(path); 401 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 402 LOG(ERROR) << "Failed to get properties for: " << path 403 << ": " << call_status; 404 return; 405 } 406 listener_->UpdateManagedStateProperties(type, path, properties); 407 408 if (properties.HasKey(shill::kIPConfigProperty)) { 409 // Since this is the first time we received properties for this network, 410 // also request its IPConfig parameters. 411 std::string ip_config_path; 412 if (properties.GetString(shill::kIPConfigProperty, &ip_config_path)) { 413 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 414 dbus::ObjectPath(ip_config_path), 415 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 416 AsWeakPtr(), path)); 417 } 418 } 419 420 // Notify the listener only when all updates for that type have completed. 421 if (pending_updates_[type].size() == 0) 422 listener_->ManagedStateListChanged(type); 423} 424 425void ShillPropertyHandler::PropertyChangedCallback( 426 ManagedState::ManagedType type, 427 const std::string& path, 428 const std::string& key, 429 const base::Value& value) { 430 if (type == ManagedState::MANAGED_TYPE_NETWORK) 431 NetworkServicePropertyChangedCallback(path, key, value); 432 else if (type == ManagedState::MANAGED_TYPE_DEVICE) 433 NetworkDevicePropertyChangedCallback(path, key, value); 434 else 435 NOTREACHED(); 436} 437 438void ShillPropertyHandler::NetworkServicePropertyChangedCallback( 439 const std::string& path, 440 const std::string& key, 441 const base::Value& value) { 442 if (key == shill::kIPConfigProperty) { 443 // Request the IPConfig for the network and update network properties 444 // when the request completes. 445 std::string ip_config_path; 446 value.GetAsString(&ip_config_path); 447 DCHECK(!ip_config_path.empty()); 448 DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( 449 dbus::ObjectPath(ip_config_path), 450 base::Bind(&ShillPropertyHandler::GetIPConfigCallback, 451 AsWeakPtr(), path)); 452 } else { 453 listener_->UpdateNetworkServiceProperty(path, key, value); 454 } 455} 456 457void ShillPropertyHandler::GetIPConfigCallback( 458 const std::string& service_path, 459 DBusMethodCallStatus call_status, 460 const base::DictionaryValue& properties) { 461 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 462 LOG(ERROR) << "Failed to get IP Config properties for: " << service_path; 463 return; 464 } 465 const base::Value* ip_address; 466 if (!properties.GetWithoutPathExpansion(flimflam::kAddressProperty, 467 &ip_address)) { 468 LOG(ERROR) << "Failed to get IP Address property for: " << service_path; 469 return; 470 } 471 listener_->UpdateNetworkServiceProperty( 472 service_path, 473 NetworkState::IPConfigProperty(flimflam::kAddressProperty), 474 *ip_address); 475 476 const base::Value* dns_servers = NULL; 477 if (!properties.GetWithoutPathExpansion( 478 flimflam::kNameServersProperty, &dns_servers)) { 479 LOG(ERROR) << "Failed to get Name servers property for: " << service_path; 480 return; 481 } 482 listener_->UpdateNetworkServiceProperty( 483 service_path, 484 NetworkState::IPConfigProperty(flimflam::kNameServersProperty), 485 *dns_servers); 486} 487 488void ShillPropertyHandler::NetworkDevicePropertyChangedCallback( 489 const std::string& path, 490 const std::string& key, 491 const base::Value& value) { 492 listener_->UpdateDeviceProperty(path, key, value); 493} 494 495} // namespace internal 496} // namespace chromeos 497