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