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