// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/connection_manager.h" #include <set> #include <string> #include <base/logging.h> #include <brillo/any.h> #include <brillo/make_unique_ptr.h> #include <brillo/message_loops/fake_message_loop.h> #include <brillo/variant_dictionary.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <shill/dbus-constants.h> #include <shill/dbus-proxies.h> #include <shill/dbus-proxy-mocks.h> #include "update_engine/common/test_utils.h" #include "update_engine/fake_shill_proxy.h" #include "update_engine/fake_system_state.h" using org::chromium::flimflam::ManagerProxyMock; using org::chromium::flimflam::ServiceProxyMock; using std::set; using std::string; using testing::Return; using testing::SetArgPointee; using testing::_; namespace chromeos_update_engine { class ConnectionManagerTest : public ::testing::Test { public: void SetUp() override { loop_.SetAsCurrent(); fake_system_state_.set_connection_manager(&cmut_); } void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); } protected: // Sets the default_service object path in the response from the // ManagerProxyMock instance. void SetManagerReply(const char* default_service, bool reply_succeeds); // Sets the |service_type|, |physical_technology| and |service_tethering| // properties in the mocked service |service_path|. If any of the three // const char* is a nullptr, the corresponding property will not be included // in the response. void SetServiceReply(const string& service_path, const char* service_type, const char* physical_technology, const char* service_tethering); void TestWithServiceType( const char* service_type, const char* physical_technology, NetworkConnectionType expected_type); void TestWithServiceTethering( const char* service_tethering, NetworkTethering expected_tethering); brillo::FakeMessageLoop loop_{nullptr}; FakeSystemState fake_system_state_; FakeShillProxy fake_shill_proxy_; // ConnectionManager under test. ConnectionManager cmut_{&fake_shill_proxy_, &fake_system_state_}; }; void ConnectionManagerTest::SetManagerReply(const char* default_service, bool reply_succeeds) { ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy(); if (!reply_succeeds) { EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) .WillOnce(Return(false)); return; } // Create a dictionary of properties and optionally include the default // service. brillo::VariantDictionary reply_dict; reply_dict["SomeOtherProperty"] = 0xC0FFEE; if (default_service) { reply_dict[shill::kDefaultServiceProperty] = dbus::ObjectPath(default_service); } EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); } void ConnectionManagerTest::SetServiceReply(const string& service_path, const char* service_type, const char* physical_technology, const char* service_tethering) { brillo::VariantDictionary reply_dict; reply_dict["SomeOtherProperty"] = 0xC0FFEE; if (service_type) reply_dict[shill::kTypeProperty] = string(service_type); if (physical_technology) { reply_dict[shill::kPhysicalTechnologyProperty] = string(physical_technology); } if (service_tethering) reply_dict[shill::kTetheringProperty] = string(service_tethering); std::unique_ptr<ServiceProxyMock> service_proxy_mock(new ServiceProxyMock()); // Plumb return value into mock object. EXPECT_CALL(*service_proxy_mock.get(), GetProperties(_, _, _)) .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); fake_shill_proxy_.SetServiceForPath(dbus::ObjectPath(service_path), std::move(service_proxy_mock)); } void ConnectionManagerTest::TestWithServiceType( const char* service_type, const char* physical_technology, NetworkConnectionType expected_type) { SetManagerReply("/service/guest/network", true); SetServiceReply("/service/guest/network", service_type, physical_technology, shill::kTetheringNotDetectedState); NetworkConnectionType type; NetworkTethering tethering; EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering)); EXPECT_EQ(expected_type, type); testing::Mock::VerifyAndClearExpectations( fake_shill_proxy_.GetManagerProxy()); } void ConnectionManagerTest::TestWithServiceTethering( const char* service_tethering, NetworkTethering expected_tethering) { SetManagerReply("/service/guest/network", true); SetServiceReply( "/service/guest/network", shill::kTypeWifi, nullptr, service_tethering); NetworkConnectionType type; NetworkTethering tethering; EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering)); EXPECT_EQ(expected_tethering, tethering); testing::Mock::VerifyAndClearExpectations( fake_shill_proxy_.GetManagerProxy()); } TEST_F(ConnectionManagerTest, SimpleTest) { TestWithServiceType(shill::kTypeEthernet, nullptr, NetworkConnectionType::kEthernet); TestWithServiceType(shill::kTypeWifi, nullptr, NetworkConnectionType::kWifi); TestWithServiceType(shill::kTypeWimax, nullptr, NetworkConnectionType::kWimax); TestWithServiceType(shill::kTypeBluetooth, nullptr, NetworkConnectionType::kBluetooth); TestWithServiceType(shill::kTypeCellular, nullptr, NetworkConnectionType::kCellular); } TEST_F(ConnectionManagerTest, PhysicalTechnologyTest) { TestWithServiceType(shill::kTypeVPN, nullptr, NetworkConnectionType::kUnknown); TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN, NetworkConnectionType::kUnknown); TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi, NetworkConnectionType::kWifi); TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax, NetworkConnectionType::kWimax); } TEST_F(ConnectionManagerTest, TetheringTest) { TestWithServiceTethering(shill::kTetheringConfirmedState, NetworkTethering::kConfirmed); TestWithServiceTethering(shill::kTetheringNotDetectedState, NetworkTethering::kNotDetected); TestWithServiceTethering(shill::kTetheringSuspectedState, NetworkTethering::kSuspected); TestWithServiceTethering("I'm not a valid property value =)", NetworkTethering::kUnknown); } TEST_F(ConnectionManagerTest, UnknownTest) { TestWithServiceType("foo", nullptr, NetworkConnectionType::kUnknown); } TEST_F(ConnectionManagerTest, AllowUpdatesOverEthernetTest) { // Updates over Ethernet are allowed even if there's no policy. EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, AllowUpdatesOverWifiTest) { EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, AllowUpdatesOverWimaxTest) { EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, BlockUpdatesOverBluetoothTest) { EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, AllowUpdatesOnlyOver3GPerPolicyTest) { policy::MockDevicePolicy allow_3g_policy; fake_system_state_.set_device_policy(&allow_3g_policy); // This test tests cellular (3G) being the only connection type being allowed. set<string> allowed_set; allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kCellular)); EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_)) .Times(1) .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true))); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, AllowUpdatesOver3GAndOtherTypesPerPolicyTest) { policy::MockDevicePolicy allow_3g_policy; fake_system_state_.set_device_policy(&allow_3g_policy); // This test tests multiple connection types being allowed, with // 3G one among them. Only Cellular is currently enforced by the policy // setting, the others are ignored (see Bluetooth for example). set<string> allowed_set; allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kCellular)); allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kBluetooth)); EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_)) .Times(3) .WillRepeatedly(DoAll(SetArgPointee<0>(allowed_set), Return(true))); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet, NetworkTethering::kUnknown)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet, NetworkTethering::kNotDetected)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi, NetworkTethering::kUnknown)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax, NetworkTethering::kUnknown)); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth, NetworkTethering::kUnknown)); // Tethered networks are treated in the same way as Cellular networks and // thus allowed. EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet, NetworkTethering::kConfirmed)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi, NetworkTethering::kConfirmed)); } TEST_F(ConnectionManagerTest, BlockUpdatesOverCellularByDefaultTest) { EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, BlockUpdatesOverTetheredNetworkByDefaultTest) { EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi, NetworkTethering::kConfirmed)); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet, NetworkTethering::kConfirmed)); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi, NetworkTethering::kSuspected)); } TEST_F(ConnectionManagerTest, BlockUpdatesOver3GPerPolicyTest) { policy::MockDevicePolicy block_3g_policy; fake_system_state_.set_device_policy(&block_3g_policy); // Test that updates for 3G are blocked while updates are allowed // over several other types. set<string> allowed_set; allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kEthernet)); allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kWifi)); allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kWimax)); EXPECT_CALL(block_3g_policy, GetAllowedConnectionTypesForUpdate(_)) .Times(1) .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true))); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, BlockUpdatesOver3GIfErrorInPolicyFetchTest) { policy::MockDevicePolicy allow_3g_policy; fake_system_state_.set_device_policy(&allow_3g_policy); set<string> allowed_set; allowed_set.insert( cmut_.StringForConnectionType(NetworkConnectionType::kCellular)); // Return false for GetAllowedConnectionTypesForUpdate and see // that updates are still blocked for 3G despite the value being in // the string set above. EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_)) .Times(1) .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(false))); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, UseUserPrefForUpdatesOverCellularIfNoPolicyTest) { policy::MockDevicePolicy no_policy; testing::NiceMock<MockPrefs>* prefs = fake_system_state_.mock_prefs(); fake_system_state_.set_device_policy(&no_policy); // No setting enforced by the device policy, user prefs should be used. EXPECT_CALL(no_policy, GetAllowedConnectionTypesForUpdate(_)) .Times(3) .WillRepeatedly(Return(false)); // No user pref: block. EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission)) .Times(1) .WillOnce(Return(false)); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); // Allow per user pref. EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _)) .Times(1) .WillOnce(DoAll(SetArgPointee<1>(true), Return(true))); EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); // Block per user pref. EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _)) .Times(1) .WillOnce(DoAll(SetArgPointee<1>(false), Return(true))); EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular, NetworkTethering::kUnknown)); } TEST_F(ConnectionManagerTest, StringForConnectionTypeTest) { EXPECT_STREQ(shill::kTypeEthernet, cmut_.StringForConnectionType(NetworkConnectionType::kEthernet)); EXPECT_STREQ(shill::kTypeWifi, cmut_.StringForConnectionType(NetworkConnectionType::kWifi)); EXPECT_STREQ(shill::kTypeWimax, cmut_.StringForConnectionType(NetworkConnectionType::kWimax)); EXPECT_STREQ(shill::kTypeBluetooth, cmut_.StringForConnectionType( NetworkConnectionType::kBluetooth)); EXPECT_STREQ(shill::kTypeCellular, cmut_.StringForConnectionType(NetworkConnectionType::kCellular)); EXPECT_STREQ("Unknown", cmut_.StringForConnectionType(NetworkConnectionType::kUnknown)); EXPECT_STREQ("Unknown", cmut_.StringForConnectionType( static_cast<NetworkConnectionType>(999999))); } TEST_F(ConnectionManagerTest, MalformedServiceList) { SetManagerReply("/service/guest/network", false); NetworkConnectionType type; NetworkTethering tethering; EXPECT_FALSE(cmut_.GetConnectionProperties(&type, &tethering)); } } // namespace chromeos_update_engine