blob: 2417be9729108a8e0ba1112a03fadb7c6c29de2c [file] [log] [blame]
// Copyright 2021 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/cellular_connection_handler.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/network/cellular_inhibitor.h"
#include "chromeos/network/fake_stub_cellular_networks_provider.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_test_helper.h"
#include "chromeos/network/test_cellular_esim_profile_handler.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace {
const char kTestCellularDevicePath[] = "cellular_path";
const char kTestCellularDeviceName[] = "cellular_name";
const char kTestBaseProfilePath[] = "profile_path_";
const char kTestBaseServicePath[] = "service_path_";
const char kTestBaseGuid[] = "guid_";
const char kTestBaseName[] = "name_";
const char kTestBaseIccid[] = "1234567890123456789";
const char kTestEuiccBasePath[] = "/org/chromium/Hermes/Euicc/";
const char kTestBaseEid[] = "12345678901234567890123456789012";
std::string CreateTestServicePath(int profile_num) {
return base::StringPrintf("%s%d", kTestBaseServicePath, profile_num);
}
std::string CreateTestProfilePath(int profile_num) {
return base::StringPrintf("%s%d", kTestBaseProfilePath, profile_num);
}
std::string CreateTestGuid(int profile_num) {
return base::StringPrintf("%s%d", kTestBaseGuid, profile_num);
}
std::string CreateTestName(int profile_num) {
return base::StringPrintf("%s%d", kTestBaseName, profile_num);
}
std::string CreateTestIccid(int profile_num) {
return base::StringPrintf("%s%d", kTestBaseIccid, profile_num);
}
std::string CreateTestEuiccPath(int euicc_num) {
return base::StringPrintf("%s%d", kTestEuiccBasePath, euicc_num);
}
std::string CreateTestEid(int euicc_num) {
return base::StringPrintf("%s%d", kTestBaseEid, euicc_num);
}
} // namespace
class CellularConnectionHandlerTest : public testing::Test {
protected:
CellularConnectionHandlerTest()
: helper_(/*use_default_devices_and_services=*/false) {}
~CellularConnectionHandlerTest() override = default;
// testing::Test:
void SetUp() override {
helper_.network_state_handler()->set_stub_cellular_networks_provider(
&fake_stubs_provider_);
inhibitor_.Init(helper_.network_state_handler(),
helper_.network_device_handler());
profile_handler_.Init(helper_.network_state_handler(), &inhibitor_);
handler_.Init(helper_.network_state_handler(), &inhibitor_,
&profile_handler_);
}
void CallPrepareExistingCellularNetworkForConnection(int profile_num) {
handler_.PrepareExistingCellularNetworkForConnection(
CreateTestIccid(profile_num),
base::BindOnce(&CellularConnectionHandlerTest::OnSuccess,
base::Unretained(this)),
base::BindOnce(&CellularConnectionHandlerTest::OnFailure,
base::Unretained(this)));
}
void CallPrepareNewlyInstalledCellularNetworkForConnection(int profile_num,
int euicc_num) {
handler_.PrepareNewlyInstalledCellularNetworkForConnection(
dbus::ObjectPath(CreateTestEuiccPath(euicc_num)),
dbus::ObjectPath(CreateTestProfilePath(profile_num)), InhibitCellular(),
base::BindOnce(&CellularConnectionHandlerTest::OnSuccess,
base::Unretained(this)),
base::BindOnce(&CellularConnectionHandlerTest::OnFailure,
base::Unretained(this)));
}
void AddCellularService(int profile_num) {
helper_.service_test()->AddService(
CreateTestServicePath(profile_num), CreateTestGuid(profile_num),
CreateTestName(profile_num), shill::kTypeCellular, shill::kStateIdle,
/*visible=*/true);
base::RunLoop().RunUntilIdle();
}
void ExpectServiceConnectable(int profile_num) {
const NetworkState* network_state =
helper_.network_state_handler()->GetNetworkState(
CreateTestServicePath(profile_num));
EXPECT_TRUE(network_state->connectable());
}
void SetServiceConnectable(int profile_num) {
helper_.service_test()->SetServiceProperty(
CreateTestServicePath(profile_num), shill::kConnectableProperty,
base::Value(true));
base::RunLoop().RunUntilIdle();
}
void SetServiceEid(int profile_num, int euicc_num) {
helper_.service_test()->SetServiceProperty(
CreateTestServicePath(profile_num), shill::kEidProperty,
base::Value(CreateTestEid(euicc_num)));
base::RunLoop().RunUntilIdle();
}
void SetServiceIccid(int profile_num) {
helper_.service_test()->SetServiceProperty(
CreateTestServicePath(profile_num), shill::kIccidProperty,
base::Value(CreateTestIccid(profile_num)));
base::RunLoop().RunUntilIdle();
}
void AddCellularDevice() {
helper_.device_test()->AddDevice(
kTestCellularDevicePath, shill::kTypeCellular, kTestCellularDeviceName);
base::RunLoop().RunUntilIdle();
}
void QueueEuiccErrorStatus() {
helper_.hermes_euicc_test()->QueueHermesErrorStatus(
HermesResponseStatus::kErrorUnknown);
}
void AddEuicc(int euicc_num) {
helper_.hermes_manager_test()->AddEuicc(
dbus::ObjectPath(CreateTestEuiccPath(euicc_num)),
CreateTestEid(euicc_num), /*is_active=*/true, /*physical_slot=*/0);
base::RunLoop().RunUntilIdle();
}
void AddProfile(int profile_num,
int euicc_num,
bool add_service = true,
bool already_enabled = false) {
auto add_profile_behavior =
add_service
? HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
kAddProfileWithService
: HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
kAddDelayedProfileWithoutService;
auto state = already_enabled ? hermes::profile::State::kActive
: hermes::profile::State::kInactive;
std::string iccid = CreateTestIccid(profile_num);
helper_.hermes_euicc_test()->AddCarrierProfile(
dbus::ObjectPath(CreateTestProfilePath(profile_num)),
dbus::ObjectPath(CreateTestEuiccPath(euicc_num)), iccid,
CreateTestName(profile_num), "service_provider", "activation_code",
CreateTestServicePath(profile_num), state,
hermes::profile::ProfileClass::kOperational, add_profile_behavior);
if (!add_service) {
fake_stubs_provider_.AddStub(iccid, CreateTestEid(euicc_num));
helper_.network_state_handler()->SyncStubCellularNetworks();
}
base::RunLoop().RunUntilIdle();
}
void AdvanceClock(base::TimeDelta time_delta) {
task_environment_.FastForwardBy(time_delta);
}
void ExpectSuccess(const std::string& expected_service_path,
base::RunLoop* run_loop) {
expected_service_path_ = expected_service_path;
on_success_callback_ = run_loop->QuitClosure();
}
void ExpectFailure(const std::string& expected_service_path,
const std::string& expected_error_name,
base::RunLoop* run_loop) {
expected_service_path_ = expected_service_path;
expected_error_name_ = expected_error_name;
on_failure_callback_ = run_loop->QuitClosure();
}
std::unique_ptr<CellularInhibitor::InhibitLock> InhibitCellular() {
base::RunLoop run_loop;
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
inhibitor_.InhibitCellularScanning(
CellularInhibitor::InhibitReason::kRemovingProfile,
base::BindLambdaForTesting(
[&](std::unique_ptr<CellularInhibitor::InhibitLock>
new_inhibit_lock) {
inhibit_lock = std::move(new_inhibit_lock);
run_loop.Quit();
}));
run_loop.Run();
return inhibit_lock;
}
void SetEnableProfileBehavior(
HermesProfileClient::TestInterface::EnableProfileBehavior behavior) {
helper_.hermes_profile_test()->SetEnableProfileBehavior(behavior);
}
void ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult result,
int expected_count = 1) {
histogram_tester_.ExpectBucketCount(
"Network.Cellular.PrepareCellularConnection.OperationResult", result,
expected_count);
}
private:
void OnSuccess(const std::string& service_path) {
EXPECT_EQ(expected_service_path_, service_path);
std::move(on_success_callback_).Run();
}
void OnFailure(const std::string& service_path,
const std::string& error_name) {
EXPECT_EQ(expected_service_path_, service_path);
EXPECT_EQ(expected_error_name_, error_name);
std::move(on_failure_callback_).Run();
}
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::HistogramTester histogram_tester_;
NetworkStateTestHelper helper_;
FakeStubCellularNetworksProvider fake_stubs_provider_;
CellularInhibitor inhibitor_;
TestCellularESimProfileHandler profile_handler_;
CellularConnectionHandler handler_;
base::OnceClosure on_success_callback_;
base::OnceClosure on_failure_callback_;
std::string expected_service_path_;
std::string expected_error_name_;
};
TEST_F(CellularConnectionHandlerTest, NoService) {
// Note: No cellular service added.
base::RunLoop run_loop;
ExpectFailure(/*service_path=*/std::string(),
NetworkConnectionHandler::kErrorNotFound, &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kCouldNotFindNetworkWithIccid);
}
TEST_F(CellularConnectionHandlerTest, ServiceAlreadyConnectable) {
AddCellularDevice();
AddCellularService(/*profile_num=*/1);
SetServiceIccid(/*profile_num=*/1);
SetServiceConnectable(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess);
}
TEST_F(CellularConnectionHandlerTest, FailsInhibiting) {
// Note: No cellular device added. This causes the inhibit operation to fail.
AddCellularService(/*profile_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectFailure(CreateTestServicePath(/*profile_num=*/1),
NetworkConnectionHandler::kErrorCellularInhibitFailure,
&run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kInhibitFailed);
}
TEST_F(CellularConnectionHandlerTest, NoRelevantEuicc) {
AddCellularDevice();
AddCellularService(/*profile_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectFailure(CreateTestServicePath(/*profile_num=*/1),
NetworkConnectionHandler::kErrorESimProfileIssue, &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kCouldNotFindRelevantEuicc);
}
TEST_F(CellularConnectionHandlerTest, FailsRequestingInstalledProfiles) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
QueueEuiccErrorStatus();
base::RunLoop run_loop;
ExpectFailure(CreateTestServicePath(/*profile_num=*/1),
NetworkConnectionHandler::kErrorESimProfileIssue, &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kRefreshProfilesFailed);
}
TEST_F(CellularConnectionHandlerTest, TimeoutWaitingForConnectable_ESim) {
const base::TimeDelta kWaitingForConnectableTimeout = base::Seconds(30);
SetEnableProfileBehavior(HermesProfileClient::TestInterface::
EnableProfileBehavior::kNotConnectable);
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectFailure(CreateTestServicePath(/*profile_num=*/1),
NetworkConnectionHandler::kConnectableCellularTimeout,
&run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
// Let all operations run, then wait for the timeout to occur.
base::RunLoop().RunUntilIdle();
AdvanceClock(kWaitingForConnectableTimeout);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kTimeoutWaitingForConnectable);
}
TEST_F(CellularConnectionHandlerTest, TimeoutWaitingForConnectable_PSim) {
const base::TimeDelta kWaitingForConnectableTimeout = base::Seconds(30);
SetEnableProfileBehavior(HermesProfileClient::TestInterface::
EnableProfileBehavior::kNotConnectable);
AddCellularDevice();
AddCellularService(/*profile_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectFailure(CreateTestServicePath(/*profile_num=*/1),
NetworkConnectionHandler::kConnectableCellularTimeout,
&run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
// Let all operations run, then wait for the timeout to occur.
base::RunLoop().RunUntilIdle();
AdvanceClock(kWaitingForConnectableTimeout);
run_loop.Run();
ExpectResult(CellularConnectionHandler::PrepareCellularConnectionResult::
kTimeoutWaitingForConnectable);
}
TEST_F(CellularConnectionHandlerTest, Success) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
run_loop.Run();
ExpectServiceConnectable(/*profile_num=*/1);
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess);
}
TEST_F(CellularConnectionHandlerTest, Success_AlreadyEnabled) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1,
/*euicc_num=*/1,
/*add_service=*/true,
/*already_enabled=*/true);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
base::RunLoop run_loop;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
SetServiceConnectable(/*profile_num=*/1);
run_loop.Run();
ExpectServiceConnectable(/*profile_num=*/1);
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess);
}
TEST_F(CellularConnectionHandlerTest, ConnectToStub) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
// Do not add a service; instead, this will cause a fake stub network to be
// created.
AddProfile(/*profile_num=*/1, /*euicc_num=*/1, /*add_service=*/false);
base::RunLoop run_loop;
// Expect that by the end, we will connect to a "real" (i.e., non-stub)
// service path.
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
base::RunLoop().RunUntilIdle();
// A connection has started to a stub. Because the profile gets enabled,
// Shill exposes a service and makes it connectable.
AddCellularService(/*profile_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
SetServiceConnectable(/*profile_num=*/1);
run_loop.Run();
ExpectServiceConnectable(/*profile_num=*/1);
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess);
}
TEST_F(CellularConnectionHandlerTest, MultipleRequests) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1, /*euicc_num=*/1);
AddProfile(/*profile_num=*/2, /*euicc_num=*/1);
SetServiceEid(/*profile_num=*/1, /*euicc_num=*/1);
SetServiceEid(/*profile_num=*/2, /*euicc_num=*/1);
SetServiceIccid(/*profile_num=*/1);
SetServiceIccid(/*profile_num=*/2);
base::RunLoop run_loop1;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop1);
// Start both operations.
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/1);
CallPrepareExistingCellularNetworkForConnection(/*profile_num=*/2);
// Verify that the first service becomes connectable.
run_loop1.Run();
ExpectServiceConnectable(/*profile_num=*/1);
base::RunLoop run_loop2;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/2), &run_loop2);
// Verify that the second service becomes connectable.
run_loop2.Run();
ExpectServiceConnectable(/*profile_num=*/2);
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess,
/*expected_count=*/2);
}
TEST_F(CellularConnectionHandlerTest, NewProfile) {
AddCellularDevice();
AddEuicc(/*euicc_num=*/1);
AddProfile(/*profile_num=*/1, /*euicc_num=*/1);
base::RunLoop run_loop;
ExpectSuccess(CreateTestServicePath(/*profile_num=*/1), &run_loop);
CallPrepareNewlyInstalledCellularNetworkForConnection(/*euicc_num=*/1,
/*profile_num=*/1);
// Verify that service corresponding to new profile becomes
// connectable.
run_loop.Run();
ExpectServiceConnectable(/*profile_num=*/1);
ExpectResult(
CellularConnectionHandler::PrepareCellularConnectionResult::kSuccess);
}
} // namespace chromeos