1 // 2 // Copyright (C) 2014 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 #include "update_engine/update_manager/real_shill_provider.h" 17 18 #include <memory> 19 #include <utility> 20 21 #include <base/memory/ptr_util.h> 22 #include <base/time/time.h> 23 #include <brillo/message_loops/fake_message_loop.h> 24 #include <gmock/gmock.h> 25 #include <gtest/gtest.h> 26 #include <shill/dbus-constants.h> 27 #include <shill/dbus-proxies.h> 28 #include <shill/dbus-proxy-mocks.h> 29 30 #include "update_engine/common/fake_clock.h" 31 #include "update_engine/common/test_utils.h" 32 #include "update_engine/dbus_test_utils.h" 33 #include "update_engine/fake_shill_proxy.h" 34 #include "update_engine/update_manager/umtest_utils.h" 35 36 using base::Time; 37 using base::TimeDelta; 38 using chromeos_update_engine::ConnectionTethering; 39 using chromeos_update_engine::ConnectionType; 40 using chromeos_update_engine::FakeClock; 41 using org::chromium::flimflam::ManagerProxyMock; 42 using org::chromium::flimflam::ServiceProxyMock; 43 using std::unique_ptr; 44 using testing::_; 45 using testing::Mock; 46 using testing::Return; 47 using testing::SetArgPointee; 48 49 namespace { 50 51 // Fake service paths. 52 const char* const kFakeEthernetServicePath = "/fake/ethernet/service"; 53 const char* const kFakeWifiServicePath = "/fake/wifi/service"; 54 const char* const kFakeWimaxServicePath = "/fake/wimax/service"; 55 const char* const kFakeBluetoothServicePath = "/fake/bluetooth/service"; 56 const char* const kFakeCellularServicePath = "/fake/cellular/service"; 57 const char* const kFakeVpnServicePath = "/fake/vpn/service"; 58 const char* const kFakeUnknownServicePath = "/fake/unknown/service"; 59 60 } // namespace 61 62 namespace chromeos_update_manager { 63 64 class UmRealShillProviderTest : public ::testing::Test { 65 protected: 66 // Initialize the RealShillProvider under test. 67 void SetUp() override { 68 fake_clock_.SetWallclockTime(InitTime()); 69 loop_.SetAsCurrent(); 70 fake_shill_proxy_ = new chromeos_update_engine::FakeShillProxy(); 71 provider_.reset(new RealShillProvider(fake_shill_proxy_, &fake_clock_)); 72 73 ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); 74 75 // The PropertyChanged signal should be subscribed to. 76 MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER( 77 manager_property_changed_, *manager_proxy_mock, PropertyChanged); 78 } 79 80 void TearDown() override { 81 provider_.reset(); 82 // Check for leaked callbacks on the main loop. 83 EXPECT_FALSE(loop_.PendingTasks()); 84 } 85 86 // These methods generate fixed timestamps for use in faking the current time. 87 Time InitTime() { 88 Time::Exploded now_exp; 89 now_exp.year = 2014; 90 now_exp.month = 3; 91 now_exp.day_of_week = 2; 92 now_exp.day_of_month = 18; 93 now_exp.hour = 8; 94 now_exp.minute = 5; 95 now_exp.second = 33; 96 now_exp.millisecond = 675; 97 Time time; 98 ignore_result(Time::FromLocalExploded(now_exp, &time)); 99 return time; 100 } 101 102 Time ConnChangedTime() { return InitTime() + TimeDelta::FromSeconds(10); } 103 104 // Sets the default_service object path in the response from the 105 // ManagerProxyMock instance. 106 void SetManagerReply(const char* default_service, bool reply_succeeds); 107 108 // Sets the |service_type|, |physical_technology| and |service_tethering| 109 // properties in the mocked service |service_path|. If any of the three 110 // const char* is a nullptr, the corresponding property will not be included 111 // in the response. 112 // Returns the mock object pointer, owned by the |fake_shill_proxy_|. 113 ServiceProxyMock* SetServiceReply(const std::string& service_path, 114 const char* service_type, 115 const char* physical_technology, 116 const char* service_tethering); 117 118 void InitWithDefaultService(const char* default_service) { 119 SetManagerReply(default_service, true); 120 // Check that provider initializes correctly. 121 EXPECT_TRUE(provider_->Init()); 122 // RunOnce to notify the signal handler was connected properly. 123 EXPECT_TRUE(loop_.RunOnce(false)); 124 } 125 126 // Sends a signal informing the provider about a default connection 127 // |service_path|. Sets the fake connection change time in 128 // |conn_change_time_p| if provided. 129 void SendDefaultServiceSignal(const std::string& service_path, 130 Time* conn_change_time_p) { 131 const Time conn_change_time = ConnChangedTime(); 132 fake_clock_.SetWallclockTime(conn_change_time); 133 ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered()); 134 manager_property_changed_.signal_callback().Run( 135 shill::kDefaultServiceProperty, dbus::ObjectPath(service_path)); 136 fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5)); 137 if (conn_change_time_p) 138 *conn_change_time_p = conn_change_time; 139 } 140 141 // Sets up expectations for detection of a connection |service_path| with type 142 // |shill_type_str| and tethering mode |shill_tethering_str|. Ensures that the 143 // new connection status and change time are properly detected by the 144 // provider. Writes the fake connection change time to |conn_change_time_p|, 145 // if provided. 146 void SetupConnectionAndAttrs(const std::string& service_path, 147 const char* shill_type, 148 const char* shill_tethering, 149 Time* conn_change_time_p) { 150 SetServiceReply(service_path, shill_type, nullptr, shill_tethering); 151 // Note: We don't setup this |service_path| as the default service path but 152 // we instead send a signal notifying the change since the code won't call 153 // GetProperties on the Manager object at this point. 154 155 // Send a signal about a new default service. 156 Time conn_change_time; 157 SendDefaultServiceSignal(service_path, &conn_change_time); 158 159 // Query the connection status, ensure last change time reported correctly. 160 UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); 161 UmTestUtils::ExpectVariableHasValue(conn_change_time, 162 provider_->var_conn_last_changed()); 163 164 // Write the connection change time to the output argument. 165 if (conn_change_time_p) 166 *conn_change_time_p = conn_change_time; 167 } 168 169 // Sets up a connection and tests that its type is being properly detected by 170 // the provider. 171 void SetupConnectionAndTestType(const char* service_path, 172 const char* shill_type, 173 ConnectionType expected_conn_type) { 174 // Set up and test the connection, record the change time. 175 Time conn_change_time; 176 SetupConnectionAndAttrs(service_path, 177 shill_type, 178 shill::kTetheringNotDetectedState, 179 &conn_change_time); 180 181 // Query the connection type, ensure last change time did not change. 182 UmTestUtils::ExpectVariableHasValue(expected_conn_type, 183 provider_->var_conn_type()); 184 UmTestUtils::ExpectVariableHasValue(conn_change_time, 185 provider_->var_conn_last_changed()); 186 } 187 188 // Sets up a connection and tests that its tethering mode is being properly 189 // detected by the provider. 190 void SetupConnectionAndTestTethering( 191 const char* service_path, 192 const char* shill_tethering, 193 ConnectionTethering expected_conn_tethering) { 194 // Set up and test the connection, record the change time. 195 Time conn_change_time; 196 SetupConnectionAndAttrs( 197 service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time); 198 199 // Query the connection tethering, ensure last change time did not change. 200 UmTestUtils::ExpectVariableHasValue(expected_conn_tethering, 201 provider_->var_conn_tethering()); 202 UmTestUtils::ExpectVariableHasValue(conn_change_time, 203 provider_->var_conn_last_changed()); 204 } 205 206 brillo::FakeMessageLoop loop_{nullptr}; 207 FakeClock fake_clock_; 208 chromeos_update_engine::FakeShillProxy* fake_shill_proxy_; 209 210 // The registered signal handler for the signal Manager.PropertyChanged. 211 chromeos_update_engine::dbus_test_utils::MockSignalHandler<void( 212 const std::string&, const brillo::Any&)> 213 manager_property_changed_; 214 215 unique_ptr<RealShillProvider> provider_; 216 }; 217 218 void UmRealShillProviderTest::SetManagerReply(const char* default_service, 219 bool reply_succeeds) { 220 ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); 221 if (!reply_succeeds) { 222 EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) 223 .WillOnce(Return(false)); 224 return; 225 } 226 227 // Create a dictionary of properties and optionally include the default 228 // service. 229 brillo::VariantDictionary reply_dict; 230 reply_dict["SomeOtherProperty"] = 0xC0FFEE; 231 232 if (default_service) { 233 reply_dict[shill::kDefaultServiceProperty] = 234 dbus::ObjectPath(default_service); 235 } 236 EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) 237 .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); 238 } 239 240 ServiceProxyMock* UmRealShillProviderTest::SetServiceReply( 241 const std::string& service_path, 242 const char* service_type, 243 const char* physical_technology, 244 const char* service_tethering) { 245 brillo::VariantDictionary reply_dict; 246 reply_dict["SomeOtherProperty"] = 0xC0FFEE; 247 248 if (service_type) 249 reply_dict[shill::kTypeProperty] = std::string(service_type); 250 251 if (physical_technology) { 252 reply_dict[shill::kPhysicalTechnologyProperty] = 253 std::string(physical_technology); 254 } 255 256 if (service_tethering) 257 reply_dict[shill::kTetheringProperty] = std::string(service_tethering); 258 259 ServiceProxyMock* service_proxy_mock = new ServiceProxyMock(); 260 261 // Plumb return value into mock object. 262 EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _)) 263 .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); 264 265 fake_shill_proxy_->SetServiceForPath(dbus::ObjectPath(service_path), 266 base::WrapUnique(service_proxy_mock)); 267 268 return service_proxy_mock; 269 } 270 271 // Query the connection status, type and time last changed, as they were set 272 // during initialization (no signals). 273 TEST_F(UmRealShillProviderTest, ReadBaseValues) { 274 InitWithDefaultService("/"); 275 // Query the provider variables. 276 UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); 277 UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); 278 UmTestUtils::ExpectVariableHasValue(InitTime(), 279 provider_->var_conn_last_changed()); 280 } 281 282 // Ensure that invalid DBus paths are ignored. 283 TEST_F(UmRealShillProviderTest, InvalidServicePath) { 284 InitWithDefaultService("invalid"); 285 UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); 286 UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); 287 UmTestUtils::ExpectVariableHasValue(InitTime(), 288 provider_->var_conn_last_changed()); 289 } 290 291 // Ensure that a service path property including a different type is ignored. 292 TEST_F(UmRealShillProviderTest, InvalidServicePathType) { 293 ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); 294 brillo::VariantDictionary reply_dict; 295 reply_dict[shill::kDefaultServiceProperty] = "/not/an/object/path"; 296 EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) 297 .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); 298 299 EXPECT_TRUE(provider_->Init()); 300 EXPECT_TRUE(loop_.RunOnce(false)); 301 302 UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); 303 } 304 305 // Test that Ethernet connection is identified correctly. 306 TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) { 307 InitWithDefaultService("/"); 308 SetupConnectionAndTestType(kFakeEthernetServicePath, 309 shill::kTypeEthernet, 310 ConnectionType::kEthernet); 311 } 312 313 // Test that Wifi connection is identified correctly. 314 TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) { 315 InitWithDefaultService("/"); 316 SetupConnectionAndTestType( 317 kFakeWifiServicePath, shill::kTypeWifi, ConnectionType::kWifi); 318 } 319 320 // Test that Wimax connection is identified correctly. 321 TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) { 322 InitWithDefaultService("/"); 323 SetupConnectionAndTestType( 324 kFakeWimaxServicePath, shill::kTypeWimax, ConnectionType::kWimax); 325 } 326 327 // Test that Bluetooth connection is identified correctly. 328 TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) { 329 InitWithDefaultService("/"); 330 SetupConnectionAndTestType(kFakeBluetoothServicePath, 331 shill::kTypeBluetooth, 332 ConnectionType::kBluetooth); 333 } 334 335 // Test that Cellular connection is identified correctly. 336 TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) { 337 InitWithDefaultService("/"); 338 SetupConnectionAndTestType(kFakeCellularServicePath, 339 shill::kTypeCellular, 340 ConnectionType::kCellular); 341 } 342 343 // Test that an unknown connection is identified as such. 344 TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) { 345 InitWithDefaultService("/"); 346 SetupConnectionAndTestType( 347 kFakeUnknownServicePath, "FooConnectionType", ConnectionType::kUnknown); 348 } 349 350 // Tests that VPN connection is identified correctly. 351 TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) { 352 InitWithDefaultService("/"); 353 // Mock logic for returning a default service path and its type. 354 SetServiceReply(kFakeVpnServicePath, 355 shill::kTypeVPN, 356 shill::kTypeWifi, 357 shill::kTetheringNotDetectedState); 358 359 // Send a signal about a new default service. 360 Time conn_change_time; 361 SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time); 362 363 // Query the connection type, ensure last change time reported correctly. 364 UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi, 365 provider_->var_conn_type()); 366 UmTestUtils::ExpectVariableHasValue(conn_change_time, 367 provider_->var_conn_last_changed()); 368 } 369 370 // Ensure that the connection type is properly cached in the provider through 371 // subsequent variable readings. 372 TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) { 373 InitWithDefaultService("/"); 374 SetupConnectionAndTestType(kFakeEthernetServicePath, 375 shill::kTypeEthernet, 376 ConnectionType::kEthernet); 377 378 UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet, 379 provider_->var_conn_type()); 380 } 381 382 // Ensure that the cached connection type remains valid even when a default 383 // connection signal occurs but the connection is not changed. 384 TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) { 385 InitWithDefaultService("/"); 386 SetupConnectionAndTestType(kFakeEthernetServicePath, 387 shill::kTypeEthernet, 388 ConnectionType::kEthernet); 389 390 SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); 391 392 UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet, 393 provider_->var_conn_type()); 394 } 395 396 // Ensure that the cached connection type is invalidated and re-read when the 397 // default connection changes. 398 TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) { 399 InitWithDefaultService("/"); 400 SetupConnectionAndTestType(kFakeEthernetServicePath, 401 shill::kTypeEthernet, 402 ConnectionType::kEthernet); 403 404 SetupConnectionAndTestType( 405 kFakeWifiServicePath, shill::kTypeWifi, ConnectionType::kWifi); 406 } 407 408 // Test that a non-tethering mode is identified correctly. 409 TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) { 410 InitWithDefaultService("/"); 411 SetupConnectionAndTestTethering(kFakeWifiServicePath, 412 shill::kTetheringNotDetectedState, 413 ConnectionTethering::kNotDetected); 414 } 415 416 // Test that a suspected tethering mode is identified correctly. 417 TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) { 418 InitWithDefaultService("/"); 419 SetupConnectionAndTestTethering(kFakeWifiServicePath, 420 shill::kTetheringSuspectedState, 421 ConnectionTethering::kSuspected); 422 } 423 424 // Test that a confirmed tethering mode is identified correctly. 425 TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) { 426 InitWithDefaultService("/"); 427 SetupConnectionAndTestTethering(kFakeWifiServicePath, 428 shill::kTetheringConfirmedState, 429 ConnectionTethering::kConfirmed); 430 } 431 432 // Test that an unknown tethering mode is identified as such. 433 TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) { 434 InitWithDefaultService("/"); 435 SetupConnectionAndTestTethering( 436 kFakeWifiServicePath, "FooConnTethering", ConnectionTethering::kUnknown); 437 } 438 439 // Ensure that the connection tethering mode is properly cached in the provider. 440 TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) { 441 InitWithDefaultService("/"); 442 SetupConnectionAndTestTethering(kFakeEthernetServicePath, 443 shill::kTetheringNotDetectedState, 444 ConnectionTethering::kNotDetected); 445 446 UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected, 447 provider_->var_conn_tethering()); 448 } 449 450 // Ensure that the cached connection tethering mode remains valid even when a 451 // default connection signal occurs but the connection is not changed. 452 TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) { 453 InitWithDefaultService("/"); 454 SetupConnectionAndTestTethering(kFakeEthernetServicePath, 455 shill::kTetheringNotDetectedState, 456 ConnectionTethering::kNotDetected); 457 458 SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); 459 460 UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected, 461 provider_->var_conn_tethering()); 462 } 463 464 // Ensure that the cached connection tethering mode is invalidated and re-read 465 // when the default connection changes. 466 TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) { 467 InitWithDefaultService("/"); 468 SetupConnectionAndTestTethering(kFakeEthernetServicePath, 469 shill::kTetheringNotDetectedState, 470 ConnectionTethering::kNotDetected); 471 472 SetupConnectionAndTestTethering(kFakeWifiServicePath, 473 shill::kTetheringConfirmedState, 474 ConnectionTethering::kConfirmed); 475 } 476 477 // Fake two DBus signals prompting a default connection change, but otherwise 478 // give the same service path. Check connection status and the time it was last 479 // changed, making sure that it is the time when the first signal was sent (and 480 // not the second). 481 TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) { 482 InitWithDefaultService("/"); 483 // Send a default service signal twice, advancing the clock in between. 484 Time conn_change_time; 485 SetupConnectionAndAttrs(kFakeEthernetServicePath, 486 shill::kTypeEthernet, 487 shill::kTetheringNotDetectedState, 488 &conn_change_time); 489 // This will set the service path to the same value, so it should not call 490 // GetProperties() again. 491 SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); 492 493 // Query the connection status, ensure last change time reported as the first 494 // time the signal was sent. 495 UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); 496 UmTestUtils::ExpectVariableHasValue(conn_change_time, 497 provider_->var_conn_last_changed()); 498 } 499 500 // Make sure that the provider initializes correctly even if shill is not 501 // responding, that variables can be obtained, and that they all return a null 502 // value (indicating that the underlying values were not set). 503 TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) { 504 // Initialize the provider, no initial connection status response. 505 SetManagerReply(nullptr, false); 506 EXPECT_TRUE(provider_->Init()); 507 EXPECT_TRUE(loop_.RunOnce(false)); 508 UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected()); 509 UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); 510 UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed()); 511 } 512 513 // Test that, once a signal is received, the connection status and other info 514 // can be read correctly. 515 TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) { 516 // Initialize the provider with no initial connection status response. 517 SetManagerReply(nullptr, false); 518 EXPECT_TRUE(provider_->Init()); 519 EXPECT_TRUE(loop_.RunOnce(false)); 520 521 SetupConnectionAndAttrs(kFakeEthernetServicePath, 522 shill::kTypeEthernet, 523 shill::kTetheringNotDetectedState, 524 nullptr); 525 UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); 526 } 527 528 } // namespace chromeos_update_manager 529