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 26 namespace { 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. 30 const size_t kMaxObserved = 100; 31 32 const 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 44 namespace chromeos { 45 namespace 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. 50 class 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 105 ShillPropertyHandler::ShillPropertyHandler(Listener* listener) 106 : listener_(listener), 107 shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) { 108 } 109 110 ShillPropertyHandler::~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 120 void ShillPropertyHandler::Init() { 121 UpdateManagerProperties(); 122 shill_manager_->AddPropertyChangedObserver(this); 123 } 124 125 void ShillPropertyHandler::UpdateManagerProperties() { 126 NET_LOG_EVENT("UpdateManagerProperties", ""); 127 shill_manager_->GetProperties( 128 base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback, 129 AsWeakPtr())); 130 } 131 132 bool ShillPropertyHandler::IsTechnologyAvailable( 133 const std::string& technology) const { 134 return available_technologies_.count(technology) != 0; 135 } 136 137 bool ShillPropertyHandler::IsTechnologyEnabled( 138 const std::string& technology) const { 139 return enabled_technologies_.count(technology) != 0; 140 } 141 142 bool ShillPropertyHandler::IsTechnologyEnabling( 143 const std::string& technology) const { 144 return enabling_technologies_.count(technology) != 0; 145 } 146 147 bool ShillPropertyHandler::IsTechnologyUninitialized( 148 const std::string& technology) const { 149 return uninitialized_technologies_.count(technology) != 0; 150 } 151 152 void 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 176 void 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 188 void 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 197 void 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 206 void 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 229 void 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 238 void 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 255 void 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 268 void 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 312 void 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 337 void 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 371 void 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 387 void 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 404 void 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 420 void 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 432 void 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 466 void 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 487 void 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 503 void 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 516 void 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 NET_LOG_ERROR("Failed to get IP Config properties", 524 base::StringPrintf("%s: %d", path.c_str(), call_status)); 525 return; 526 } 527 NET_LOG_EVENT("IP Config properties received", path); 528 listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties); 529 } 530 531 } // namespace internal 532 } // namespace chromeos 533