blob: 50231f10d5b857af9e12e4ea63d6a9028f3063c1 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/network/network_connection_handler_impl.h"
#include <map>
#include <memory>
#include <set>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/task_environment.h"
#include "chromeos/network/cellular_connection_handler.h"
#include "chromeos/network/cellular_inhibitor.h"
#include "chromeos/network/cellular_utils.h"
#include "chromeos/network/managed_network_configuration_handler_impl.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_connection_observer.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_test_helper.h"
#include "chromeos/network/onc/network_onc_utils.h"
#include "chromeos/network/prohibited_technologies_handler.h"
#include "chromeos/network/stub_cellular_networks_provider.h"
#include "chromeos/network/system_token_cert_db_storage.h"
#include "chromeos/network/test_cellular_esim_profile_handler.h"
#include "components/onc/onc_constants.h"
#include "crypto/scoped_nss_types.h"
#include "crypto/scoped_test_nss_db.h"
#include "net/base/net_errors.h"
#include "net/cert/nss_cert_database_chromeos.h"
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace {
const char kSuccessResult[] = "success";
const char kTetherGuid[] = "tether-guid";
const char kTestCellularGuid[] = "cellular_guid";
const char kTestCellularDevicePath[] = "cellular_path";
const char kTestCellularDeviceName[] = "cellular_name";
const char kTestCellularServicePath[] = "cellular_service_path";
const char kTestCellularName[] = "cellular_name";
const char kTestIccid[] = "1234567890123456789";
const char kTestEuiccPath[] = "/org/chromium/Hermes/Euicc/1";
const char kTestEid[] = "123456789012345678901234567890123";
const char kTestCellularServicePath2[] = "cellular_service_path_2";
const char kTestIccid2[] = "9876543210987654321";
class TestNetworkConnectionObserver : public NetworkConnectionObserver {
public:
TestNetworkConnectionObserver() = default;
TestNetworkConnectionObserver(const TestNetworkConnectionObserver&) = delete;
TestNetworkConnectionObserver& operator=(
const TestNetworkConnectionObserver&) = delete;
~TestNetworkConnectionObserver() override = default;
// NetworkConnectionObserver
void ConnectToNetworkRequested(const std::string& service_path) override {
requests_.insert(service_path);
}
void ConnectSucceeded(const std::string& service_path) override {
results_[service_path] = kSuccessResult;
}
void ConnectFailed(const std::string& service_path,
const std::string& error_name) override {
results_[service_path] = error_name;
}
void DisconnectRequested(const std::string& service_path) override {
requests_.insert(service_path);
disconnect_requests_.insert(service_path);
}
bool GetRequested(const std::string& service_path) {
return requests_.count(service_path) != 0;
}
std::string GetResult(const std::string& service_path) {
auto iter = results_.find(service_path);
if (iter == results_.end())
return "";
return iter->second;
}
const std::set<std::string>& disconnect_requests() {
return disconnect_requests_;
}
private:
std::set<std::string> disconnect_requests_;
std::set<std::string> requests_;
std::map<std::string, std::string> results_;
};
class FakeTetherDelegate : public NetworkConnectionHandler::TetherDelegate {
public:
FakeTetherDelegate()
: last_delegate_function_type_(DelegateFunctionType::NONE) {}
~FakeTetherDelegate() override = default;
enum class DelegateFunctionType { NONE, CONNECT, DISCONNECT };
DelegateFunctionType last_delegate_function_type() {
return last_delegate_function_type_;
}
void ConnectToNetwork(const std::string& service_path,
base::OnceClosure success_callback,
StringErrorCallback error_callback) override {
last_delegate_function_type_ = DelegateFunctionType::CONNECT;
last_service_path_ = service_path;
last_success_callback_ = std::move(success_callback);
last_error_callback_ = std::move(error_callback);
}
void DisconnectFromNetwork(const std::string& service_path,
base::OnceClosure success_callback,
StringErrorCallback error_callback) override {
last_delegate_function_type_ = DelegateFunctionType::DISCONNECT;
last_service_path_ = service_path;
last_success_callback_ = std::move(success_callback);
last_error_callback_ = std::move(error_callback);
}
std::string& last_service_path() { return last_service_path_; }
base::OnceClosure& last_success_callback() { return last_success_callback_; }
StringErrorCallback& last_error_callback() { return last_error_callback_; }
private:
std::string last_service_path_;
base::OnceClosure last_success_callback_;
StringErrorCallback last_error_callback_;
DelegateFunctionType last_delegate_function_type_;
};
} // namespace
class NetworkConnectionHandlerImplTest : public testing::Test {
public:
NetworkConnectionHandlerImplTest() = default;
NetworkConnectionHandlerImplTest(const NetworkConnectionHandlerImplTest&) =
delete;
NetworkConnectionHandlerImplTest& operator=(
const NetworkConnectionHandlerImplTest&) = delete;
~NetworkConnectionHandlerImplTest() override = default;
void SetUp() override {
ASSERT_TRUE(test_nssdb_.is_open());
// Use the same DB for public and private slot.
test_nsscertdb_ = std::make_unique<net::NSSCertDatabaseChromeOS>(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())),
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())));
SystemTokenCertDbStorage::Initialize();
NetworkCertLoader::Initialize();
NetworkCertLoader::ForceAvailableForNetworkAuthForTesting();
LoginState::Initialize();
}
void Init(bool use_cellular_connection_handler = true) {
network_config_handler_ = NetworkConfigurationHandler::InitializeForTest(
helper_.network_state_handler(), nullptr /* network_device_handler */);
network_profile_handler_.reset(new NetworkProfileHandler());
network_profile_handler_->Init();
managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
managed_config_handler_->Init(
/*cellular_policy_handler=*/nullptr, helper_.network_state_handler(),
network_profile_handler_.get(), network_config_handler_.get(),
nullptr /* network_device_handler */,
nullptr /* prohibited_tecnologies_handler */);
cellular_inhibitor_ = std::make_unique<CellularInhibitor>();
cellular_inhibitor_->Init(helper_.network_state_handler(),
helper_.network_device_handler());
cellular_esim_profile_handler_ =
std::make_unique<TestCellularESimProfileHandler>();
cellular_esim_profile_handler_->Init(helper_.network_state_handler(),
cellular_inhibitor_.get());
stub_cellular_networks_provider_ =
std::make_unique<StubCellularNetworksProvider>();
stub_cellular_networks_provider_->Init(
helper_.network_state_handler(), cellular_esim_profile_handler_.get());
cellular_connection_handler_.reset(new CellularConnectionHandler());
cellular_connection_handler_->Init(helper_.network_state_handler(),
cellular_inhibitor_.get(),
cellular_esim_profile_handler_.get());
network_connection_handler_ =
std::make_unique<NetworkConnectionHandlerImpl>();
network_connection_handler_->Init(
helper_.network_state_handler(), network_config_handler_.get(),
managed_config_handler_.get(),
use_cellular_connection_handler ? cellular_connection_handler_.get()
: nullptr);
network_connection_observer_.reset(new TestNetworkConnectionObserver);
network_connection_handler_->AddObserver(
network_connection_observer_.get());
task_environment_.RunUntilIdle();
fake_tether_delegate_ = std::make_unique<FakeTetherDelegate>();
}
void TearDown() override {
helper_.hermes_euicc_test()->SetInteractiveDelay(base::Seconds(0));
helper_.manager_test()->SetInteractiveDelay(base::Seconds(0));
managed_config_handler_.reset();
network_profile_handler_.reset();
network_connection_handler_->RemoveObserver(
network_connection_observer_.get());
network_connection_observer_.reset();
network_connection_handler_.reset();
network_config_handler_.reset();
LoginState::Shutdown();
NetworkCertLoader::Shutdown();
SystemTokenCertDbStorage::Shutdown();
}
protected:
std::string ServicePathFromGuid(const std::string& guid) {
std::string service_path =
helper_.service_test()->FindServiceMatchingGUID(guid);
EXPECT_FALSE(service_path.empty());
return service_path;
}
void Connect(const std::string& service_path) {
const base::TimeDelta kProfileRefreshCallbackDelay =
base::Milliseconds(150);
network_connection_handler_->ConnectToNetwork(
service_path,
base::BindOnce(&NetworkConnectionHandlerImplTest::SuccessCallback,
base::Unretained(this)),
base::BindOnce(&NetworkConnectionHandlerImplTest::ErrorCallback,
base::Unretained(this)),
true /* check_error_state */, ConnectCallbackMode::ON_COMPLETED);
// Connect can result in two profile refresh calls before and after
// enabling profile. Fast forward by delay after refresh.
task_environment_.FastForwardBy(2 * kProfileRefreshCallbackDelay);
task_environment_.RunUntilIdle();
}
void Disconnect(const std::string& service_path) {
network_connection_handler_->DisconnectNetwork(
service_path,
base::BindOnce(&NetworkConnectionHandlerImplTest::SuccessCallback,
base::Unretained(this)),
base::BindOnce(&NetworkConnectionHandlerImplTest::ErrorCallback,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
void SuccessCallback() { result_ = kSuccessResult; }
void ErrorCallback(const std::string& error_name) { result_ = error_name; }
std::string GetResultAndReset() {
std::string result;
result.swap(result_);
return result;
}
void StartNetworkCertLoader() {
NetworkCertLoader::Get()->SetUserNSSDB(test_nsscertdb_.get());
task_environment_.RunUntilIdle();
}
void LoginToRegularUser() {
LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE,
LoginState::LOGGED_IN_USER_REGULAR);
task_environment_.RunUntilIdle();
}
scoped_refptr<net::X509Certificate> ImportTestClientCert() {
net::ScopedCERTCertificateList ca_cert_list =
net::CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "client_1_ca.pem",
net::X509Certificate::FORMAT_AUTO);
if (ca_cert_list.empty()) {
LOG(ERROR) << "No CA cert loaded.";
return nullptr;
}
net::NSSCertDatabase::ImportCertFailureList failures;
EXPECT_TRUE(test_nsscertdb_->ImportCACerts(
ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures));
if (!failures.empty()) {
LOG(ERROR) << net::ErrorToString(failures[0].net_error);
return nullptr;
}
// Import a client cert signed by that CA.
scoped_refptr<net::X509Certificate> client_cert(
net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
"client_1.pem", "client_1.pk8",
test_nssdb_.slot()));
return client_cert;
}
void SetupUserPolicy(const std::string& network_configs_json) {
base::Value network_configs(base::Value::Type::LIST);
if (!network_configs_json.empty()) {
base::JSONReader::ValueWithError parsed_json =
base::JSONReader::ReadAndReturnValueWithError(
network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS);
ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
ASSERT_TRUE(parsed_json.value->is_list());
network_configs = std::move(*parsed_json.value);
}
managed_config_handler_->SetPolicy(
::onc::ONC_SOURCE_USER_POLICY, helper_.UserHash(), network_configs,
/*global_config=*/base::Value(base::Value::Type::DICTIONARY));
task_environment_.RunUntilIdle();
}
void SetupDevicePolicy(const std::string& network_configs_json,
const base::Value& global_config) {
base::Value network_configs(base::Value::Type::LIST);
if (!network_configs_json.empty()) {
base::JSONReader::ValueWithError parsed_json =
base::JSONReader::ReadAndReturnValueWithError(
network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS);
ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
ASSERT_TRUE(parsed_json.value->is_list());
network_configs = std::move(*parsed_json.value);
}
managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY,
std::string(), // no username hash
network_configs, global_config);
task_environment_.RunUntilIdle();
}
std::string ConfigureService(const std::string& shill_json_string) {
return helper_.ConfigureService(shill_json_string);
}
std::string ConfigureVpnServiceWithProviderType(
const std::string& vpn_provider_type) {
const std::string kVpnGuid = "vpn_guid";
const std::string kShillJsonStringTemplate =
R"({"GUID": "$1", "Type": "vpn", "State": "idle",
"Provider": {"Type": "$2", "Host": "host"}})";
const std::string shill_json_string = base::ReplaceStringPlaceholders(
kShillJsonStringTemplate, {kVpnGuid, vpn_provider_type},
/*offsets=*/nullptr);
return ConfigureService(shill_json_string);
}
std::string GetServiceStringProperty(const std::string& service_path,
const std::string& key) {
return helper_.GetServiceStringProperty(service_path, key);
}
void QueueEuiccErrorStatus() {
helper_.hermes_euicc_test()->QueueHermesErrorStatus(
HermesResponseStatus::kErrorUnknown);
}
void SetCellularServiceConnectable(
const std::string& service_path = kTestCellularServicePath) {
helper_.service_test()->SetServiceProperty(
service_path, shill::kConnectableProperty, base::Value(true));
base::RunLoop().RunUntilIdle();
}
void SetCellularServiceState(const std::string& state) {
helper_.service_test()->SetServiceProperty(
kTestCellularServicePath, shill::kStateProperty, base::Value(state));
base::RunLoop().RunUntilIdle();
}
void SetCellularServiceOutOfCredits() {
helper_.service_test()->SetServiceProperty(kTestCellularServicePath,
shill::kOutOfCreditsProperty,
base::Value(true));
base::RunLoop().RunUntilIdle();
}
void SetCellularSimLocked() {
// Simulate a locked SIM.
base::Value sim_lock_status(base::Value::Type::DICTIONARY);
sim_lock_status.SetKey(shill::kSIMLockTypeProperty,
base::Value(shill::kSIMLockPin));
helper_.device_test()->SetDeviceProperty(
kTestCellularDevicePath, shill::kSIMLockStatusProperty,
std::move(sim_lock_status), /*notify_changed=*/true);
// Set the cellular service to be the active profile.
base::Value::ListStorage sim_slot_infos;
base::Value slot_info_item(base::Value::Type::DICTIONARY);
slot_info_item.SetKey(shill::kSIMSlotInfoICCID, base::Value(kTestIccid));
slot_info_item.SetBoolKey(shill::kSIMSlotInfoPrimary, true);
sim_slot_infos.push_back(std::move(slot_info_item));
helper_.device_test()->SetDeviceProperty(
kTestCellularDevicePath, shill::kSIMSlotInfoProperty,
base::Value(sim_slot_infos), /*notify_changed=*/true);
base::RunLoop().RunUntilIdle();
}
void AddNonConnectablePSimService() {
AddCellularDevice();
AddCellularService(/*has_eid=*/false);
}
void AddNonConnectableESimService() {
AddCellularDevice();
AddCellularService(/*has_eid=*/true);
}
void AddCellularServiceWithESimProfile(bool is_stub = false) {
AddCellularDevice();
// Add EUICC which will hold the profile.
helper_.hermes_manager_test()->AddEuicc(dbus::ObjectPath(kTestEuiccPath),
kTestEid, /*is_active=*/true,
/*physical_slot=*/0);
HermesEuiccClient::TestInterface::AddCarrierProfileBehavior behavior =
is_stub ? HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
kAddProfileWithoutService
: HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
kAddProfileWithService;
helper_.hermes_euicc_test()->AddCarrierProfile(
dbus::ObjectPath(kTestCellularServicePath),
dbus::ObjectPath(kTestEuiccPath), kTestIccid, kTestCellularName,
"service_provider", "activation_code", kTestCellularServicePath,
hermes::profile::State::kInactive,
hermes::profile::ProfileClass::kOperational, behavior);
base::RunLoop().RunUntilIdle();
}
void AddCellularService(
bool has_eid,
const std::string& service_path = kTestCellularServicePath,
const std::string& iccid = kTestIccid) {
// Add idle, non-connectable network.
helper_.service_test()->AddService(service_path, kTestCellularGuid,
kTestCellularName, shill::kTypeCellular,
shill::kStateIdle, /*visible=*/true);
if (has_eid) {
helper_.service_test()->SetServiceProperty(
service_path, shill::kEidProperty, base::Value(kTestEid));
}
helper_.service_test()->SetServiceProperty(
service_path, shill::kIccidProperty, base::Value(iccid));
base::RunLoop().RunUntilIdle();
}
// Used when testing a code that accesses NetworkHandler::Get() directly (e.g.
// when checking if VPN is disabled by policy when attempting to connect to a
// VPN network). NetworkStateTestHelper can not be used here. That's because
// NetworkStateTestHelper initializes a NetworkStateHandler for testing, but
// NetworkHandler::Initialize() constructs its own NetworkStateHandler
// instance and NetworkHandler::Get() uses it.
// Note: Tests using this method must call NetworkHandler::Shutdown() before
// returning.
void ProhibitVpnForNetworkHandler() {
NetworkHandler::Initialize();
NetworkHandler::Get()
->prohibited_technologies_handler()
->AddGloballyProhibitedTechnology(shill::kTypeVPN);
}
void AdvanceClock(base::TimeDelta time_delta) {
task_environment_.FastForwardBy(time_delta);
}
void SetShillConnectError(const std::string& error_name) {
helper_.service_test()->SetErrorForNextConnectionAttempt(error_name);
}
NetworkStateHandler* network_state_handler() {
return helper_.network_state_handler();
}
TestNetworkConnectionObserver* network_connection_observer() {
return network_connection_observer_.get();
}
NetworkConnectionHandler* network_connection_handler() {
return network_connection_handler_.get();
}
FakeTetherDelegate* fake_tether_delegate() {
return fake_tether_delegate_.get();
}
private:
void AddCellularDevice() {
helper_.device_test()->AddDevice(
kTestCellularDevicePath, shill::kTypeCellular, kTestCellularDeviceName);
base::RunLoop().RunUntilIdle();
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
NetworkStateTestHelper helper_{false /* use_default_devices_and_services */};
std::unique_ptr<NetworkConfigurationHandler> network_config_handler_;
std::unique_ptr<NetworkConnectionHandler> network_connection_handler_;
std::unique_ptr<TestNetworkConnectionObserver> network_connection_observer_;
std::unique_ptr<ManagedNetworkConfigurationHandlerImpl>
managed_config_handler_;
std::unique_ptr<CellularInhibitor> cellular_inhibitor_;
std::unique_ptr<TestCellularESimProfileHandler>
cellular_esim_profile_handler_;
std::unique_ptr<StubCellularNetworksProvider>
stub_cellular_networks_provider_;
std::unique_ptr<CellularConnectionHandler> cellular_connection_handler_;
std::unique_ptr<NetworkProfileHandler> network_profile_handler_;
crypto::ScopedTestNSSDB test_nssdb_;
std::unique_ptr<net::NSSCertDatabaseChromeOS> test_nsscertdb_;
std::string result_;
std::unique_ptr<FakeTetherDelegate> fake_tether_delegate_;
};
namespace {
const char* kNoNetwork = "no-network";
const char* kConfigWifi0Connectable =
"{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
" \"Connectable\": true }";
const char* kConfigWifi1Connected =
"{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"online\" }";
const char* kConfigWifi2Connecting =
"{ \"GUID\": \"wifi2\", \"Type\": \"wifi\", \"State\": \"association\" }";
const char* kConfigWifi3RequiresPassphrase =
"{ \"GUID\": \"wifi3\", \"Type\": \"wifi\", "
" \"PassphraseRequired\": true }";
const char* kPolicyWifi0 =
"[{ \"GUID\": \"wifi0\", \"IPAddressConfigType\": \"DHCP\", "
" \"Type\": \"WiFi\", \"Name\": \"My WiFi Network\","
" \"WiFi\": { \"SSID\": \"wifi0\"}}]";
} // namespace
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerConnectSuccess) {
Init();
std::string wifi0_service_path = ConfigureService(kConfigWifi0Connectable);
ASSERT_FALSE(wifi0_service_path.empty());
Connect(wifi0_service_path);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
EXPECT_EQ(
shill::kStateOnline,
GetServiceStringProperty(wifi0_service_path, shill::kStateProperty));
// Observer expectations
EXPECT_TRUE(network_connection_observer()->GetRequested(wifi0_service_path));
EXPECT_EQ(kSuccessResult,
network_connection_observer()->GetResult(wifi0_service_path));
}
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerConnectBlockedByManagedOnly) {
Init();
std::string wifi0_service_path = ConfigureService(kConfigWifi0Connectable);
ASSERT_FALSE(wifi0_service_path.empty());
base::Value global_config(base::Value::Type::DICTIONARY);
global_config.SetKey(
::onc::global_network_config::kAllowOnlyPolicyWiFiToConnect,
base::Value(true));
SetupDevicePolicy("[]", global_config);
SetupUserPolicy("[]");
LoginToRegularUser();
Connect(wifi0_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
SetupDevicePolicy(kPolicyWifi0, global_config);
Connect(wifi0_service_path);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerConnectBlockedBySSID) {
Init();
std::string wifi0_service_path = ConfigureService(kConfigWifi0Connectable);
ASSERT_FALSE(wifi0_service_path.empty());
// Set a device policy which blocks wifi0.
base::Value::ListStorage blocked;
blocked.push_back(base::Value("7769666930")); // hex(wifi0) = 7769666930
base::Value global_config(base::Value::Type::DICTIONARY);
global_config.SetKey(::onc::global_network_config::kBlockedHexSSIDs,
base::Value(blocked));
SetupDevicePolicy("[]", global_config);
SetupUserPolicy("[]");
LoginToRegularUser();
Connect(wifi0_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
// Set a user policy, which configures wifi0 (==allowed).
SetupUserPolicy(kPolicyWifi0);
Connect(wifi0_service_path);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
// Handles basic failure cases.
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerConnectFailure) {
Init();
Connect(kNoNetwork);
EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kNoNetwork));
EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
network_connection_observer()->GetResult(kNoNetwork));
std::string wifi1_service_path = ConfigureService(kConfigWifi1Connected);
ASSERT_FALSE(wifi1_service_path.empty());
Connect(wifi1_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnected, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(wifi1_service_path));
EXPECT_EQ(NetworkConnectionHandler::kErrorConnected,
network_connection_observer()->GetResult(wifi1_service_path));
std::string wifi2_service_path = ConfigureService(kConfigWifi2Connecting);
ASSERT_FALSE(wifi2_service_path.empty());
Connect(wifi2_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnecting, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(wifi2_service_path));
EXPECT_EQ(NetworkConnectionHandler::kErrorConnecting,
network_connection_observer()->GetResult(wifi2_service_path));
std::string wifi3_service_path =
ConfigureService(kConfigWifi3RequiresPassphrase);
Connect(wifi3_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorPassphraseRequired,
GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(wifi3_service_path));
EXPECT_EQ(NetworkConnectionHandler::kErrorPassphraseRequired,
network_connection_observer()->GetResult(wifi3_service_path));
}
TEST_F(NetworkConnectionHandlerImplTest,
IgnoreConnectInProgressError_Succeeds) {
Init();
AddCellularServiceWithESimProfile();
// Verify the result is not returned and observers are not called if shill
// returns InProgress error.
SetShillConnectError(shill::kErrorResultInProgress);
Connect(kTestCellularServicePath);
EXPECT_TRUE(GetResultAndReset().empty());
EXPECT_TRUE(network_connection_observer()
->GetResult(kTestCellularServicePath)
.empty());
// Verify that connect request returns when service state changes to
// connected.
SetCellularServiceState(shill::kStateOnline);
EXPECT_EQ(kSuccessResult,
network_connection_observer()->GetResult(kTestCellularServicePath));
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, IgnoreConnectInProgressError_Fails) {
Init();
AddCellularServiceWithESimProfile();
SetShillConnectError(shill::kErrorResultInProgress);
Connect(kTestCellularServicePath);
EXPECT_TRUE(GetResultAndReset().empty());
EXPECT_TRUE(network_connection_observer()
->GetResult(kTestCellularServicePath)
.empty());
// Set cellular service to connecting state.
SetCellularServiceState(shill::kStateAssociation);
// Verify the connect request fails with error when returned and observers are
// not called if shill returns InProgress error.
SetCellularServiceState(shill::kStateIdle);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
network_connection_observer()->GetResult(kTestCellularServicePath));
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
}
namespace {
const char* kPolicyWithCertPatternTemplate =
"[ { \"GUID\": \"wifi4\","
" \"Name\": \"wifi4\","
" \"Type\": \"WiFi\","
" \"WiFi\": {"
" \"Security\": \"WPA-EAP\","
" \"SSID\": \"wifi_ssid\","
" \"EAP\": {"
" \"Outer\": \"EAP-TLS\","
" \"ClientCertType\": \"Pattern\","
" \"ClientCertPattern\": {"
" \"Subject\": {"
" \"CommonName\" : \"%s\""
" }"
" }"
" }"
" }"
"} ]";
} // namespace
// Handle certificates.
TEST_F(NetworkConnectionHandlerImplTest, ConnectCertificateMissing) {
Init();
StartNetworkCertLoader();
SetupUserPolicy(
base::StringPrintf(kPolicyWithCertPatternTemplate, "unknown"));
Connect(ServicePathFromGuid("wifi4"));
EXPECT_EQ(NetworkConnectionHandler::kErrorCertificateRequired,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, ConnectWithCertificateSuccess) {
Init();
StartNetworkCertLoader();
scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
ASSERT_TRUE(cert.get());
SetupUserPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
cert->subject().common_name.c_str()));
Connect(ServicePathFromGuid("wifi4"));
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectWithCertificateRequestedWhenCertsCanNotBeAvailable) {
Init();
scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
ASSERT_TRUE(cert.get());
SetupUserPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
cert->subject().common_name.c_str()));
Connect(ServicePathFromGuid("wifi4"));
// Connect request came when no client certificates can exist because
// NetworkCertLoader doesn't have a NSSCertDatabase configured and also has
// not notified that a NSSCertDatabase is being initialized.
EXPECT_EQ(NetworkConnectionHandler::kErrorCertificateRequired,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectWithCertificateRequestedBeforeCertsAreLoaded) {
Init();
scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
ASSERT_TRUE(cert.get());
SetupUserPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
cert->subject().common_name.c_str()));
// Mark that a user slot NSSCertDatabase is being initialized so that
// NetworkConnectionHandler attempts to wait for certificates to be loaded.
NetworkCertLoader::Get()->MarkUserNSSDBWillBeInitialized();
Connect(ServicePathFromGuid("wifi4"));
// Connect request came before the cert loader loaded certificates, so the
// connect request should have been throttled until the certificates are
// loaded.
EXPECT_EQ("", GetResultAndReset());
StartNetworkCertLoader();
// |StartNetworkCertLoader| should have triggered certificate loading.
// When the certificates got loaded, the connection request should have
// proceeded and eventually succeeded.
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectWithCertificateRequestedBeforeCertsAreLoaded_NeverLoaded) {
const base::TimeDelta kMaxCertLoadTimeSeconds = base::Seconds(15);
Init();
scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
ASSERT_TRUE(cert.get());
SetupUserPolicy(base::StringPrintf(kPolicyWithCertPatternTemplate,
cert->subject().common_name.c_str()));
// Mark that a user slot NSSCertDatabase is being initialized so that
// NetworkConnectionHandler attempts to wait for certificates to be loaded.
NetworkCertLoader::Get()->MarkUserNSSDBWillBeInitialized();
Connect(ServicePathFromGuid("wifi4"));
// Connect request came before the cert loader loaded certificates, so the
// connect request should have been throttled until the certificates are
// loaded.
EXPECT_EQ("", GetResultAndReset());
AdvanceClock(kMaxCertLoadTimeSeconds);
// The result should indicate a certificate load timeout.
EXPECT_EQ(NetworkConnectionHandler::kErrorCertLoadTimeout,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerDisconnectSuccess) {
Init();
std::string wifi1_service_path = ConfigureService(kConfigWifi1Connected);
ASSERT_FALSE(wifi1_service_path.empty());
Disconnect(wifi1_service_path);
EXPECT_TRUE(network_connection_observer()->GetRequested(wifi1_service_path));
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
NetworkConnectionHandlerDisconnectFailure) {
Init();
Connect(kNoNetwork);
EXPECT_EQ(NetworkConnectionHandler::kErrorConfigureFailed,
GetResultAndReset());
std::string wifi0_service_path = ConfigureService(kConfigWifi0Connectable);
ASSERT_FALSE(wifi0_service_path.empty());
Disconnect(wifi0_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorNotConnected, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, ConnectToTetherNetwork_Success) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
network_connection_handler()->SetTetherDelegate(fake_tether_delegate());
// For tether networks, guid == service_path.
Connect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::CONNECT,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
std::move(fake_tether_delegate()->last_success_callback()).Run();
EXPECT_EQ(kSuccessResult, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(kSuccessResult,
network_connection_observer()->GetResult(kTetherGuid));
}
TEST_F(NetworkConnectionHandlerImplTest, ConnectToTetherNetwork_Failure) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
network_connection_handler()->SetTetherDelegate(fake_tether_delegate());
// For tether networks, guid == service_path.
Connect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::CONNECT,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
std::move(fake_tether_delegate()->last_error_callback())
.Run(NetworkConnectionHandler::kErrorConnectFailed);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
network_connection_observer()->GetResult(kTetherGuid));
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToIkev2VpnNetworkWhenProhibited_Failure) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderIKEv2);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToL2tpIpsecVpnNetworkWhenProhibited_Failure) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderL2tpIpsec);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToOpenVpnNetworkWhenProhibited_Failure) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderOpenVpn);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToWireGuardNetworkWhenProhibited_Failure) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderWireGuard);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(NetworkConnectionHandler::kErrorBlockedByPolicy,
GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToThirdPartyVpnNetworkWhenProhibited_Success) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderThirdPartyVpn);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToArcVpnNetworkWhenProhibited_Success) {
Init();
ProhibitVpnForNetworkHandler();
const std::string vpn_service_path =
ConfigureVpnServiceWithProviderType(shill::kProviderArcVpn);
ASSERT_FALSE(vpn_service_path.empty());
Connect(/*service_path=*/vpn_service_path);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
NetworkHandler::Shutdown();
}
TEST_F(NetworkConnectionHandlerImplTest,
ConnectToTetherNetwork_NoTetherDelegate) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
// Do not set a tether delegate.
// For tether networks, guid == service_path.
Connect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::NONE,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(NetworkConnectionHandler::kErrorTetherAttemptWithNoDelegate,
GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(NetworkConnectionHandler::kErrorTetherAttemptWithNoDelegate,
network_connection_observer()->GetResult(kTetherGuid));
}
TEST_F(NetworkConnectionHandlerImplTest, DisconnectFromTetherNetwork_Success) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
network_state_handler()->SetTetherNetworkStateConnecting(kTetherGuid);
network_connection_handler()->SetTetherDelegate(fake_tether_delegate());
// For tether networks, guid == service_path.
Disconnect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::DISCONNECT,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
std::move(fake_tether_delegate()->last_success_callback()).Run();
EXPECT_EQ(kSuccessResult, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(kSuccessResult,
network_connection_observer()->GetResult(kTetherGuid));
}
TEST_F(NetworkConnectionHandlerImplTest, DisconnectFromTetherNetwork_Failure) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
network_state_handler()->SetTetherNetworkStateConnecting(kTetherGuid);
network_connection_handler()->SetTetherDelegate(fake_tether_delegate());
// For tether networks, guid == service_path.
Disconnect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::DISCONNECT,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
std::move(fake_tether_delegate()->last_error_callback())
.Run(NetworkConnectionHandler::kErrorConnectFailed);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
network_connection_observer()->GetResult(kTetherGuid));
}
TEST_F(NetworkConnectionHandlerImplTest,
DisconnectFromTetherNetwork_NoTetherDelegate) {
Init();
network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TECHNOLOGY_ENABLED);
network_state_handler()->AddTetherNetworkState(
kTetherGuid, "TetherNetwork", "Carrier", 100 /* battery_percentage */,
100 /* signal_strength */, true /* has_connected_to_host */);
network_state_handler()->SetTetherNetworkStateConnecting(kTetherGuid);
// Do not set a tether delegate.
// For tether networks, guid == service_path.
Disconnect(kTetherGuid /* service_path */);
EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::NONE,
fake_tether_delegate()->last_delegate_function_type());
EXPECT_EQ(NetworkConnectionHandler::kErrorTetherAttemptWithNoDelegate,
GetResultAndReset());
EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
EXPECT_EQ(NetworkConnectionHandler::kErrorTetherAttemptWithNoDelegate,
network_connection_observer()->GetResult(kTetherGuid));
}
// Regression test for b/186381398.
TEST_F(NetworkConnectionHandlerImplTest,
PSimProfile_NoCellularConnectionHandler) {
Init(/*use_cellular_connection_handler=*/false);
AddNonConnectablePSimService();
SetCellularServiceConnectable();
Connect(kTestCellularServicePath);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, PSimProfile_NotConnectable) {
Init();
AddNonConnectablePSimService();
Connect(kTestCellularServicePath);
SetCellularServiceConnectable();
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, PSimProfile_OutOfCredits) {
Init();
AddNonConnectablePSimService();
SetCellularServiceOutOfCredits();
Connect(kTestCellularServicePath);
EXPECT_EQ(NetworkConnectionHandler::kErrorCellularOutOfCredits,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, SimLocked) {
Init();
AddNonConnectablePSimService();
SetCellularSimLocked();
SetCellularServiceConnectable();
Connect(kTestCellularServicePath);
EXPECT_EQ(NetworkConnectionHandler::kErrorSimLocked, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, ESimProfile_AlreadyConnectable) {
Init();
AddCellularServiceWithESimProfile();
// Set the service to be connectable before trying to connect. This does not
// invoke the CellularConnectionHandler flow since the profile is already
// enabled.
SetCellularServiceConnectable();
Connect(kTestCellularServicePath);
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, ESimProfile_EnableProfile) {
Init();
AddCellularServiceWithESimProfile();
// Do not set the service to be connectable before trying to connect. When a
// connection is initiated, we attempt to enable the profile via Hermes.
Connect(kTestCellularServicePath);
SetCellularServiceConnectable();
EXPECT_EQ(kSuccessResult, GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, ESimProfile_StubToShillBacked) {
Init();
AddCellularServiceWithESimProfile(/*is_stub=*/true);
// Connect to a stub path. Internally, this should wait until a connectable
// Shill-backed service is created.
Connect(GenerateStubCellularServicePath(kTestIccid));
// Now, create a non-stub service and make it connectable.
AddNonConnectableESimService();
SetCellularServiceConnectable();
EXPECT_EQ(kSuccessResult, GetResultAndReset());
// A connection was requested to the stub service path, not the actual one.
EXPECT_TRUE(network_connection_observer()->GetRequested(
GenerateStubCellularServicePath(kTestIccid)));
EXPECT_FALSE(
network_connection_observer()->GetRequested(kTestCellularServicePath));
// However, the connection success was part of the actual service path, not
// the stub one.
EXPECT_EQ(std::string(), network_connection_observer()->GetResult(
GenerateStubCellularServicePath(kTestIccid)));
EXPECT_EQ(kSuccessResult,
network_connection_observer()->GetResult(kTestCellularServicePath));
}
TEST_F(NetworkConnectionHandlerImplTest, ESimProfile_EnableProfile_Fails) {
Init();
AddCellularServiceWithESimProfile();
// Queue an error which should cause enabling the profile to fail.
QueueEuiccErrorStatus();
// Do not set the service to be connectable before trying to connect. When a
// connection is initiated, we attempt to enable the profile via Hermes.
Connect(kTestCellularServicePath);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(NetworkConnectionHandler::kErrorESimProfileIssue,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, MultipleCellularConnect) {
Init();
AddCellularServiceWithESimProfile();
AddCellularService(/*has_eid=*/false, kTestCellularServicePath2, kTestIccid2);
// Delay hermes operation so that first connect will be waiting in
// CellularConnectionHandler.
HermesEuiccClient::Get()->GetTestInterface()->SetInteractiveDelay(
base::Seconds(10));
Connect(kTestCellularServicePath);
Connect(kTestCellularServicePath2);
// Verify that second connect request fails with device busy.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(NetworkConnectionHandler::kErrorCellularDeviceBusy,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest, CellularConnectTimeout) {
const base::TimeDelta kCellularConnectTimeout = base::Seconds(150);
Init();
AddNonConnectablePSimService();
SetCellularServiceConnectable(kTestCellularServicePath);
ShillManagerClient::Get()->GetTestInterface()->SetInteractiveDelay(
base::Seconds(200));
Connect(kTestCellularServicePath);
AdvanceClock(kCellularConnectTimeout);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectTimeout,
GetResultAndReset());
}
TEST_F(NetworkConnectionHandlerImplTest,
CellularConnectTimeout_StubToShillBacked) {
const base::TimeDelta kCellularConnectTimeout = base::Seconds(150);
Init();
AddCellularServiceWithESimProfile(/*is_stub=*/true);
// Connect to a stub path. Internally, this should wait until a connectable
// Shill-backed service is created.
Connect(GenerateStubCellularServicePath(kTestIccid));
// Now, Create a shill backed service for the same network.
ShillManagerClient::Get()->GetTestInterface()->SetInteractiveDelay(
base::Seconds(200));
AddNonConnectableESimService();
SetCellularServiceConnectable();
// Verify that connection timesout properly even when network path
// transitioned.
AdvanceClock(kCellularConnectTimeout);
EXPECT_EQ(NetworkConnectionHandler::kErrorConnectTimeout,
GetResultAndReset());
}
} // namespace chromeos