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