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 <map> 8 #include <set> 9 #include <string> 10 11 #include "base/bind.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.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 "dbus/object_path.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 #include "third_party/cros_system_api/dbus/service_constants.h" 24 25 namespace chromeos { 26 27 namespace { 28 29 void DoNothingWithCallStatus(DBusMethodCallStatus call_status) { 30 } 31 32 void ErrorCallbackFunction(const std::string& error_name, 33 const std::string& error_message) { 34 LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message; 35 } 36 37 class TestListener : public internal::ShillPropertyHandler::Listener { 38 public: 39 TestListener() : manager_updates_(0), errors_(0) { 40 } 41 42 virtual void UpdateManagedList(ManagedState::ManagedType type, 43 const base::ListValue& entries) OVERRIDE { 44 UpdateEntries(GetTypeString(type), entries); 45 } 46 47 virtual void UpdateManagedStateProperties( 48 ManagedState::ManagedType type, 49 const std::string& path, 50 const base::DictionaryValue& properties) OVERRIDE { 51 AddInitialPropertyUpdate(GetTypeString(type), path); 52 } 53 54 virtual void ProfileListChanged() OVERRIDE { 55 } 56 57 virtual void UpdateNetworkServiceProperty( 58 const std::string& service_path, 59 const std::string& key, 60 const base::Value& value) OVERRIDE { 61 AddPropertyUpdate(flimflam::kServicesProperty, service_path); 62 } 63 64 virtual void UpdateDeviceProperty( 65 const std::string& device_path, 66 const std::string& key, 67 const base::Value& value) OVERRIDE { 68 AddPropertyUpdate(flimflam::kDevicesProperty, device_path); 69 } 70 71 virtual void NotifyManagerPropertyChanged() OVERRIDE { 72 ++manager_updates_; 73 } 74 75 virtual void CheckPortalListChanged( 76 const std::string& check_portal_list) OVERRIDE { 77 } 78 79 virtual void ManagedStateListChanged( 80 ManagedState::ManagedType type) OVERRIDE { 81 AddStateListUpdate(GetTypeString(type)); 82 } 83 84 std::vector<std::string>& entries(const std::string& type) { 85 return entries_[type]; 86 } 87 std::map<std::string, int>& property_updates(const std::string& type) { 88 return property_updates_[type]; 89 } 90 std::map<std::string, int>& initial_property_updates( 91 const std::string& type) { 92 return initial_property_updates_[type]; 93 } 94 int list_updates(const std::string& type) { return list_updates_[type]; } 95 int manager_updates() { return manager_updates_; } 96 int errors() { return errors_; } 97 98 private: 99 std::string GetTypeString(ManagedState::ManagedType type) { 100 if (type == ManagedState::MANAGED_TYPE_NETWORK) { 101 return flimflam::kServicesProperty; 102 } else if (type == ManagedState::MANAGED_TYPE_FAVORITE) { 103 return shill::kServiceCompleteListProperty; 104 } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { 105 return flimflam::kDevicesProperty; 106 } 107 LOG(ERROR) << "UpdateManagedList called with unrecognized type: " << type; 108 ++errors_; 109 return std::string(); 110 } 111 112 void UpdateEntries(const std::string& type, const base::ListValue& entries) { 113 if (type.empty()) 114 return; 115 entries_[type].clear(); 116 for (base::ListValue::const_iterator iter = entries.begin(); 117 iter != entries.end(); ++iter) { 118 std::string path; 119 if ((*iter)->GetAsString(&path)) 120 entries_[type].push_back(path); 121 } 122 } 123 124 void AddPropertyUpdate(const std::string& type, const std::string& path) { 125 if (type.empty()) 126 return; 127 property_updates(type)[path] += 1; 128 } 129 130 void AddInitialPropertyUpdate(const std::string& type, 131 const std::string& path) { 132 if (type.empty()) 133 return; 134 initial_property_updates(type)[path] += 1; 135 } 136 137 void AddStateListUpdate(const std::string& type) { 138 if (type.empty()) 139 return; 140 list_updates_[type] += 1; 141 } 142 143 // Map of list-type -> paths 144 std::map<std::string, std::vector<std::string> > entries_; 145 // Map of list-type -> map of paths -> update counts 146 std::map<std::string, std::map<std::string, int> > property_updates_; 147 std::map<std::string, std::map<std::string, int> > initial_property_updates_; 148 // Map of list-type -> list update counts 149 std::map<std::string, int > list_updates_; 150 int manager_updates_; 151 int errors_; 152 }; 153 154 } // namespace 155 156 class ShillPropertyHandlerTest : public testing::Test { 157 public: 158 ShillPropertyHandlerTest() 159 : manager_test_(NULL), 160 device_test_(NULL), 161 service_test_(NULL), 162 profile_test_(NULL) { 163 } 164 virtual ~ShillPropertyHandlerTest() { 165 } 166 167 virtual void SetUp() OVERRIDE { 168 // Initialize DBusThreadManager with a stub implementation. 169 DBusThreadManager::InitializeWithStub(); 170 // Get the test interface for manager / device / service and clear the 171 // default stub properties. 172 manager_test_ = 173 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface(); 174 ASSERT_TRUE(manager_test_); 175 device_test_ = 176 DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface(); 177 ASSERT_TRUE(device_test_); 178 service_test_ = 179 DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); 180 ASSERT_TRUE(service_test_); 181 profile_test_ = 182 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); 183 ASSERT_TRUE(profile_test_); 184 SetupShillPropertyHandler(); 185 message_loop_.RunUntilIdle(); 186 } 187 188 virtual void TearDown() OVERRIDE { 189 shill_property_handler_.reset(); 190 listener_.reset(); 191 DBusThreadManager::Shutdown(); 192 } 193 194 void AddDevice(const std::string& type, const std::string& id) { 195 ASSERT_TRUE(IsValidType(type)); 196 device_test_->AddDevice(id, type, std::string("/device/" + id)); 197 } 198 199 void RemoveDevice(const std::string& id) { 200 device_test_->RemoveDevice(id); 201 } 202 203 void AddService(const std::string& type, 204 const std::string& id, 205 const std::string& state, 206 bool add_to_watch_list) { 207 ASSERT_TRUE(IsValidType(type)); 208 service_test_->AddService(id, id, type, state, 209 true /* visible */, add_to_watch_list); 210 } 211 212 void AddServiceWithIPConfig(const std::string& type, 213 const std::string& id, 214 const std::string& state, 215 const std::string& ipconfig_path, 216 bool add_to_watch_list) { 217 ASSERT_TRUE(IsValidType(type)); 218 service_test_->AddServiceWithIPConfig(id, id, type, state, 219 ipconfig_path, 220 true /* visible */, 221 add_to_watch_list); 222 } 223 224 void AddServiceToProfile(const std::string& type, 225 const std::string& id, 226 bool visible) { 227 service_test_->AddService(id, id, type, flimflam::kStateIdle, 228 visible, false /* watch */); 229 std::vector<std::string> profiles; 230 profile_test_->GetProfilePaths(&profiles); 231 ASSERT_TRUE(profiles.size() > 0); 232 base::DictionaryValue properties; // Empty entry 233 profile_test_->AddService(profiles[0], id); 234 } 235 236 void RemoveService(const std::string& id) { 237 service_test_->RemoveService(id); 238 } 239 240 // Call this after any initial Shill client setup 241 void SetupShillPropertyHandler() { 242 SetupDefaultShillState(); 243 listener_.reset(new TestListener); 244 shill_property_handler_.reset( 245 new internal::ShillPropertyHandler(listener_.get())); 246 shill_property_handler_->Init(); 247 } 248 249 bool IsValidType(const std::string& type) { 250 return (type == flimflam::kTypeEthernet || 251 type == flimflam::kTypeWifi || 252 type == flimflam::kTypeWimax || 253 type == flimflam::kTypeBluetooth || 254 type == flimflam::kTypeCellular || 255 type == flimflam::kTypeVPN); 256 } 257 258 protected: 259 void SetupDefaultShillState() { 260 message_loop_.RunUntilIdle(); // Process any pending updates 261 device_test_->ClearDevices(); 262 AddDevice(flimflam::kTypeWifi, "stub_wifi_device1"); 263 AddDevice(flimflam::kTypeCellular, "stub_cellular_device1"); 264 service_test_->ClearServices(); 265 const bool add_to_watchlist = true; 266 AddService(flimflam::kTypeEthernet, "stub_ethernet", 267 flimflam::kStateOnline, add_to_watchlist); 268 AddService(flimflam::kTypeWifi, "stub_wifi1", 269 flimflam::kStateOnline, add_to_watchlist); 270 AddService(flimflam::kTypeWifi, "stub_wifi2", 271 flimflam::kStateIdle, add_to_watchlist); 272 AddService(flimflam::kTypeCellular, "stub_cellular1", 273 flimflam::kStateIdle, add_to_watchlist); 274 } 275 276 base::MessageLoopForUI message_loop_; 277 scoped_ptr<TestListener> listener_; 278 scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_; 279 ShillManagerClient::TestInterface* manager_test_; 280 ShillDeviceClient::TestInterface* device_test_; 281 ShillServiceClient::TestInterface* service_test_; 282 ShillProfileClient::TestInterface* profile_test_; 283 284 private: 285 DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest); 286 }; 287 288 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) { 289 EXPECT_EQ(1, listener_->manager_updates()); 290 EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable( 291 flimflam::kTypeWifi)); 292 EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled( 293 flimflam::kTypeWifi)); 294 const size_t kNumShillManagerClientStubImplDevices = 2; 295 EXPECT_EQ(kNumShillManagerClientStubImplDevices, 296 listener_->entries(flimflam::kDevicesProperty).size()); 297 const size_t kNumShillManagerClientStubImplServices = 4; 298 EXPECT_EQ(kNumShillManagerClientStubImplServices, 299 listener_->entries(flimflam::kServicesProperty).size()); 300 301 EXPECT_EQ(0, listener_->errors()); 302 } 303 304 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) { 305 EXPECT_EQ(1, listener_->manager_updates()); 306 307 // Remove a technology. Updates both the Available and Enabled lists. 308 manager_test_->RemoveTechnology(flimflam::kTypeWimax); 309 message_loop_.RunUntilIdle(); 310 EXPECT_EQ(3, listener_->manager_updates()); 311 312 // Add a disabled technology. 313 manager_test_->AddTechnology(flimflam::kTypeWimax, false); 314 message_loop_.RunUntilIdle(); 315 EXPECT_EQ(4, listener_->manager_updates()); 316 EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable( 317 flimflam::kTypeWimax)); 318 EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled( 319 flimflam::kTypeWimax)); 320 321 // Enable the technology. 322 DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology( 323 flimflam::kTypeWimax, 324 base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); 325 message_loop_.RunUntilIdle(); 326 EXPECT_EQ(5, listener_->manager_updates()); 327 EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled( 328 flimflam::kTypeWimax)); 329 330 EXPECT_EQ(0, listener_->errors()); 331 } 332 333 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) { 334 EXPECT_EQ(1, listener_->manager_updates()); 335 EXPECT_EQ(1, listener_->list_updates(flimflam::kDevicesProperty)); 336 const size_t kNumShillManagerClientStubImplDevices = 2; 337 EXPECT_EQ(kNumShillManagerClientStubImplDevices, 338 listener_->entries(flimflam::kDevicesProperty).size()); 339 // Add a device. 340 const std::string kTestDevicePath("test_wifi_device1"); 341 AddDevice(flimflam::kTypeWifi, kTestDevicePath); 342 message_loop_.RunUntilIdle(); 343 EXPECT_EQ(1, listener_->manager_updates()); // No new manager updates. 344 EXPECT_EQ(2, listener_->list_updates(flimflam::kDevicesProperty)); 345 EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1, 346 listener_->entries(flimflam::kDevicesProperty).size()); 347 // Device changes are not observed. 348 // Remove a device 349 RemoveDevice(kTestDevicePath); 350 message_loop_.RunUntilIdle(); 351 EXPECT_EQ(3, listener_->list_updates(flimflam::kDevicesProperty)); 352 EXPECT_EQ(kNumShillManagerClientStubImplDevices, 353 listener_->entries(flimflam::kDevicesProperty).size()); 354 355 EXPECT_EQ(0, listener_->errors()); 356 } 357 358 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) { 359 EXPECT_EQ(1, listener_->manager_updates()); 360 EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty)); 361 const size_t kNumShillManagerClientStubImplServices = 4; 362 EXPECT_EQ(kNumShillManagerClientStubImplServices, 363 listener_->entries(flimflam::kServicesProperty).size()); 364 365 // Add an unwatched service. 366 const std::string kTestServicePath("test_wifi_service1"); 367 AddService(flimflam::kTypeWifi, kTestServicePath, 368 flimflam::kStateIdle, false); 369 message_loop_.RunUntilIdle(); 370 EXPECT_EQ(1, listener_->manager_updates()); // No new manager updates. 371 // Watched and unwatched services trigger a service list update. 372 EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty)); 373 EXPECT_EQ(kNumShillManagerClientStubImplServices + 1, 374 listener_->entries(flimflam::kServicesProperty).size()); 375 // Service receives an initial property update. 376 EXPECT_EQ(1, listener_->initial_property_updates( 377 flimflam::kServicesProperty)[kTestServicePath]); 378 // Change a property. 379 base::FundamentalValue scan_interval(3); 380 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 381 dbus::ObjectPath(kTestServicePath), 382 flimflam::kScanIntervalProperty, 383 scan_interval, 384 base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); 385 message_loop_.RunUntilIdle(); 386 // Property change triggers an update. 387 EXPECT_EQ(1, listener_->property_updates( 388 flimflam::kServicesProperty)[kTestServicePath]); 389 390 // Add the existing service to the watch list. 391 AddService(flimflam::kTypeWifi, kTestServicePath, 392 flimflam::kStateIdle, true); 393 message_loop_.RunUntilIdle(); 394 // Service list update should be received when watch list changes. 395 EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty)); 396 // Number of services shouldn't change. 397 EXPECT_EQ(kNumShillManagerClientStubImplServices + 1, 398 listener_->entries(flimflam::kServicesProperty).size()); 399 400 // Change a property. 401 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 402 dbus::ObjectPath(kTestServicePath), 403 flimflam::kScanIntervalProperty, 404 scan_interval, 405 base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); 406 message_loop_.RunUntilIdle(); 407 // Property change should trigger another update. 408 EXPECT_EQ(2, listener_->property_updates( 409 flimflam::kServicesProperty)[kTestServicePath]); 410 411 // Remove a service 412 RemoveService(kTestServicePath); 413 message_loop_.RunUntilIdle(); 414 EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty)); 415 EXPECT_EQ(kNumShillManagerClientStubImplServices, 416 listener_->entries(flimflam::kServicesProperty).size()); 417 418 EXPECT_EQ(0, listener_->errors()); 419 } 420 421 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerIPConfigPropertyChanged) { 422 // Set the properties for an IP Config object. 423 const std::string kTestIPConfigPath("test_ip_config_path"); 424 425 base::StringValue ip_address("192.168.1.1"); 426 DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( 427 dbus::ObjectPath(kTestIPConfigPath), 428 flimflam::kAddressProperty, ip_address, 429 base::Bind(&DoNothingWithCallStatus)); 430 base::ListValue dns_servers; 431 dns_servers.Append(base::Value::CreateStringValue("192.168.1.100")); 432 dns_servers.Append(base::Value::CreateStringValue("192.168.1.101")); 433 DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( 434 dbus::ObjectPath(kTestIPConfigPath), 435 flimflam::kNameServersProperty, dns_servers, 436 base::Bind(&DoNothingWithCallStatus)); 437 base::FundamentalValue prefixlen(8); 438 DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( 439 dbus::ObjectPath(kTestIPConfigPath), 440 flimflam::kPrefixlenProperty, prefixlen, 441 base::Bind(&DoNothingWithCallStatus)); 442 base::StringValue gateway("192.0.0.1"); 443 DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty( 444 dbus::ObjectPath(kTestIPConfigPath), 445 flimflam::kGatewayProperty, gateway, 446 base::Bind(&DoNothingWithCallStatus)); 447 message_loop_.RunUntilIdle(); 448 449 // Add a service with an empty ipconfig and then update 450 // its ipconfig property. 451 const std::string kTestServicePath1("test_wifi_service1"); 452 AddService(flimflam::kTypeWifi, kTestServicePath1, 453 flimflam::kStateIdle, true); 454 message_loop_.RunUntilIdle(); 455 // This is the initial property update. 456 EXPECT_EQ(1, listener_->initial_property_updates( 457 flimflam::kServicesProperty)[kTestServicePath1]); 458 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 459 dbus::ObjectPath(kTestServicePath1), 460 shill::kIPConfigProperty, 461 base::StringValue(kTestIPConfigPath), 462 base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); 463 message_loop_.RunUntilIdle(); 464 // IPConfig property change on the service should trigger property updates for 465 // IP Address, DNS, prefixlen, and gateway. 466 EXPECT_EQ(4, listener_->property_updates( 467 flimflam::kServicesProperty)[kTestServicePath1]); 468 469 // Now, Add a new watched service with the IPConfig already set. 470 const std::string kTestServicePath2("test_wifi_service2"); 471 AddServiceWithIPConfig(flimflam::kTypeWifi, kTestServicePath2, 472 flimflam::kStateIdle, kTestIPConfigPath, true); 473 message_loop_.RunUntilIdle(); 474 // A watched service with the IPConfig property already set must trigger 475 // property updates for IP Address, DNS, prefixlen, and gateway when added. 476 EXPECT_EQ(4, listener_->property_updates( 477 flimflam::kServicesProperty)[kTestServicePath2]); 478 } 479 480 TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServiceCompleteList) { 481 // Initial list updates. 482 EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty)); 483 EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty)); 484 485 // Add a new entry to the profile only; should trigger a single list update 486 // for both Services and ServiceCompleteList, and a single property update 487 // for ServiceCompleteList. 488 const std::string kTestServicePath1("stub_wifi_profile_only1"); 489 AddServiceToProfile(flimflam::kTypeWifi, kTestServicePath1, false); 490 shill_property_handler_->UpdateManagerProperties(); 491 message_loop_.RunUntilIdle(); 492 EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty)); 493 EXPECT_EQ(2, listener_->list_updates(shill::kServiceCompleteListProperty)); 494 EXPECT_EQ(0, listener_->initial_property_updates( 495 flimflam::kServicesProperty)[kTestServicePath1]); 496 EXPECT_EQ(1, listener_->initial_property_updates( 497 shill::kServiceCompleteListProperty)[kTestServicePath1]); 498 EXPECT_EQ(0, listener_->property_updates( 499 flimflam::kServicesProperty)[kTestServicePath1]); 500 EXPECT_EQ(0, listener_->property_updates( 501 shill::kServiceCompleteListProperty)[kTestServicePath1]); 502 503 // Add a new entry to the services and the profile; should also trigger a 504 // single list update for both Services and ServiceCompleteList, and should 505 // trigger tow property updates for Services (one when the Profile propety 506 // changes, and one for the Request) and one ServiceCompleteList change for 507 // the Request. 508 const std::string kTestServicePath2("stub_wifi_profile_only2"); 509 AddServiceToProfile(flimflam::kTypeWifi, kTestServicePath2, true); 510 shill_property_handler_->UpdateManagerProperties(); 511 message_loop_.RunUntilIdle(); 512 EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty)); 513 EXPECT_EQ(3, listener_->list_updates(shill::kServiceCompleteListProperty)); 514 EXPECT_EQ(1, listener_->initial_property_updates( 515 flimflam::kServicesProperty)[kTestServicePath2]); 516 EXPECT_EQ(1, listener_->initial_property_updates( 517 shill::kServiceCompleteListProperty)[kTestServicePath2]); 518 // Expect one property update for the Profile property of the Network. 519 EXPECT_EQ(1, listener_->property_updates( 520 flimflam::kServicesProperty)[kTestServicePath2]); 521 EXPECT_EQ(0, listener_->property_updates( 522 shill::kServiceCompleteListProperty)[kTestServicePath2]); 523 524 // Change a property of a Network in a Profile. 525 base::FundamentalValue scan_interval(3); 526 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 527 dbus::ObjectPath(kTestServicePath2), 528 flimflam::kScanIntervalProperty, 529 scan_interval, 530 base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); 531 message_loop_.RunUntilIdle(); 532 // Property change should trigger an update for the Network only; no 533 // property updates pushed by Shill affect Favorites. 534 EXPECT_EQ(2, listener_->property_updates( 535 flimflam::kServicesProperty)[kTestServicePath2]); 536 EXPECT_EQ(0, listener_->property_updates( 537 shill::kServiceCompleteListProperty)[kTestServicePath2]); 538 } 539 540 } // namespace chromeos 541