blob: d2955d15ee73660279b6e5c437f71a6d8a9f97ab [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "shill/cellular/cellular_service.h"
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include "shill/cellular/cellular_capability.h"
#include "shill/cellular/mock_cellular.h"
#include "shill/cellular/mock_mobile_operator_info.h"
#include "shill/cellular/mock_modem_info.h"
#include "shill/fake_store.h"
#include "shill/mock_adaptors.h"
#include "shill/mock_control.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_profile.h"
#include "shill/service_property_change_test.h"
using std::string;
using testing::_;
using testing::AnyNumber;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
namespace shill {
namespace {
const char kImsi[] = "111222123456789";
const char kIccid[] = "1234567890000";
} // namespace
class CellularServiceTest : public testing::Test {
public:
CellularServiceTest()
: manager_(&control_, &dispatcher_, &metrics_),
modem_info_(&control_, &manager_),
adaptor_(nullptr) {
Service::SetNextSerialNumberForTesting(0);
}
~CellularServiceTest() override { adaptor_ = nullptr; }
void SetUp() override {
// Many tests set service properties which call Manager.UpdateService().
EXPECT_CALL(manager_, UpdateService(_)).Times(AnyNumber());
device_ = new MockCellular(&modem_info_, "usb0", kAddress, 3,
Cellular::kTypeCdma, "", RpcIdentifier(""));
// CellularService expects an IMSI and SIM ID be set in the Device.
device_->set_imsi(kImsi);
device_->set_iccid(kIccid);
service_ =
new CellularService(&manager_, kImsi, kIccid, device_->GetSimCardId());
service_->SetDevice(device_.get());
adaptor_ = static_cast<ServiceMockAdaptor*>(service_->adaptor());
storage_id_ = service_->GetStorageIdentifier();
storage_.SetString(storage_id_, CellularService::kStorageType,
kTypeCellular);
storage_.SetString(storage_id_, CellularService::kStorageIccid, kIccid);
storage_.SetString(storage_id_, CellularService::kStorageImsi, kImsi);
}
protected:
static const char kAddress[];
string GetFriendlyName() const { return service_->friendly_name(); }
bool IsAutoConnectable(const char** reason) const {
return service_->IsAutoConnectable(reason);
}
bool SetAutoConnectFull(bool connect) {
return service_->SetAutoConnectFull(connect, /*error=*/nullptr);
}
EventDispatcher dispatcher_;
NiceMock<MockControl> control_;
MockMetrics metrics_;
NiceMock<MockManager> manager_;
MockModemInfo modem_info_;
scoped_refptr<MockCellular> device_;
CellularServiceRefPtr service_;
ServiceMockAdaptor* adaptor_; // Owned by |service_|.
std::string storage_id_;
FakeStore storage_;
};
const char CellularServiceTest::kAddress[] = "000102030405";
TEST_F(CellularServiceTest, Constructor) {
EXPECT_TRUE(service_->connectable());
}
TEST_F(CellularServiceTest, SetNetworkTechnology) {
EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty,
kNetworkTechnologyUmts));
EXPECT_TRUE(service_->network_technology().empty());
service_->SetNetworkTechnology(kNetworkTechnologyUmts);
EXPECT_EQ(kNetworkTechnologyUmts, service_->network_technology());
service_->SetNetworkTechnology(kNetworkTechnologyUmts);
}
TEST_F(CellularServiceTest, LogName) {
EXPECT_EQ("cellular_0", service_->log_name());
service_->SetNetworkTechnology(kNetworkTechnologyUmts);
EXPECT_EQ("cellular_UMTS_0", service_->log_name());
service_->SetNetworkTechnology(kNetworkTechnologyGsm);
EXPECT_EQ("cellular_GSM_0", service_->log_name());
service_->SetNetworkTechnology(kNetworkTechnologyLte);
EXPECT_EQ("cellular_LTE_0", service_->log_name());
}
TEST_F(CellularServiceTest, SetRoamingState) {
EXPECT_CALL(*adaptor_,
EmitStringChanged(kRoamingStateProperty, kRoamingStateHome));
EXPECT_TRUE(service_->roaming_state().empty());
service_->SetRoamingState(kRoamingStateHome);
EXPECT_EQ(kRoamingStateHome, service_->roaming_state());
service_->SetRoamingState(kRoamingStateHome);
}
TEST_F(CellularServiceTest, SetServingOperator) {
static const char kCode[] = "123456";
static const char kName[] = "Some Cellular Operator";
Stringmap test_operator;
service_->SetServingOperator(test_operator);
test_operator[kOperatorCodeKey] = kCode;
test_operator[kOperatorNameKey] = kName;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kServingOperatorProperty, _));
service_->SetServingOperator(test_operator);
const Stringmap& serving_operator = service_->serving_operator();
ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorCodeKey));
ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorNameKey));
EXPECT_EQ(kCode, serving_operator.find(kOperatorCodeKey)->second);
EXPECT_EQ(kName, serving_operator.find(kOperatorNameKey)->second);
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kServingOperatorProperty, _))
.Times(0);
service_->SetServingOperator(serving_operator);
}
TEST_F(CellularServiceTest, SetOLP) {
const char kMethod[] = "GET";
const char kURL[] = "payment.url";
const char kPostData[] = "post_man";
Stringmap olp;
service_->SetOLP("", "", "");
olp = service_->olp(); // Copy to simplify assertions below.
EXPECT_EQ("", olp[kPaymentPortalURL]);
EXPECT_EQ("", olp[kPaymentPortalMethod]);
EXPECT_EQ("", olp[kPaymentPortalPostData]);
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kPaymentPortalProperty, _));
service_->SetOLP(kURL, kMethod, kPostData);
olp = service_->olp(); // Copy to simplify assertions below.
EXPECT_EQ(kURL, olp[kPaymentPortalURL]);
EXPECT_EQ(kMethod, olp[kPaymentPortalMethod]);
EXPECT_EQ(kPostData, olp[kPaymentPortalPostData]);
}
TEST_F(CellularServiceTest, SetUsageURL) {
static const char kUsageURL[] = "usage.url";
EXPECT_CALL(*adaptor_, EmitStringChanged(kUsageURLProperty, kUsageURL));
EXPECT_TRUE(service_->usage_url().empty());
service_->SetUsageURL(kUsageURL);
EXPECT_EQ(kUsageURL, service_->usage_url());
service_->SetUsageURL(kUsageURL);
}
TEST_F(CellularServiceTest, SetApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
service_->set_profile(profile);
Error error;
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
service_->SetApn(testapn, &error);
EXPECT_TRUE(error.IsSuccess());
Stringmap resultapn = service_->GetApn(&error);
EXPECT_TRUE(error.IsSuccess());
Stringmap::const_iterator it = resultapn.find(kApnProperty);
EXPECT_TRUE(it != resultapn.end() && it->second == kApn);
it = resultapn.find(kApnUsernameProperty);
EXPECT_TRUE(it != resultapn.end() && it->second == kUsername);
EXPECT_NE(nullptr, service_->GetUserSpecifiedApn());
}
TEST_F(CellularServiceTest, ClearApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
service_->set_profile(profile);
Error error;
// Set up an APN to make sure that it later gets cleared.
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
service_->SetApn(testapn, &error);
Stringmap resultapn = service_->GetApn(&error);
ASSERT_TRUE(error.IsSuccess());
Stringmap emptyapn;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularLastGoodApnProperty, _))
.Times(0);
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _))
.Times(1);
service_->SetApn(emptyapn, &error);
EXPECT_TRUE(error.IsSuccess());
resultapn = service_->GetApn(&error);
EXPECT_TRUE(resultapn.empty());
EXPECT_EQ(nullptr, service_->GetUserSpecifiedApn());
}
TEST_F(CellularServiceTest, LastGoodApn) {
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
service_->set_profile(profile);
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularLastGoodApnProperty, _));
service_->SetLastGoodApn(testapn);
Stringmap* resultapn = service_->GetLastGoodApn();
ASSERT_NE(nullptr, resultapn);
EXPECT_EQ(kApn, (*resultapn)[kApnProperty]);
EXPECT_EQ(kUsername, (*resultapn)[kApnUsernameProperty]);
// Now set the user-specified APN, and check that LastGoodApn is preserved.
Stringmap userapn;
userapn[kApnProperty] = kApn;
userapn[kApnUsernameProperty] = kUsername;
EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
Error error;
service_->SetApn(userapn, &error);
ASSERT_NE(nullptr, service_->GetLastGoodApn());
EXPECT_EQ(kApn, (*resultapn)[kApnProperty]);
EXPECT_EQ(kUsername, (*resultapn)[kApnUsernameProperty]);
}
TEST_F(CellularServiceTest, IsAutoConnectable) {
// This test assumes AutoConnect is not disabled by policy.
EXPECT_CALL(manager_, IsTechnologyAutoConnectDisabled(_))
.WillRepeatedly(Return(false));
const char* reason = nullptr;
// Auto-connect should be suppressed if the device is not running.
device_->running_ = false;
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnDeviceDisabled, reason);
device_->running_ = true;
// If we're in a process of activation, don't auto-connect.
EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
GetActivationState(_, _))
.WillOnce(Return(PendingActivationStore::kStatePending));
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnActivating, reason);
EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
GetActivationState(_, _))
.WillRepeatedly(Return(PendingActivationStore::kStateActivated));
// Auto-connect should be suppressed if we're out of credits.
service_->NotifySubscriptionStateChanged(SubscriptionState::kOutOfCredits);
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnOutOfCredits, reason);
service_->NotifySubscriptionStateChanged(SubscriptionState::kProvisioned);
// A PPP authentication failure means the Service is not auto-connectable.
service_->SetFailure(Service::kFailurePPPAuth);
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(CellularService::kAutoConnBadPPPCredentials, reason);
// Reset failure state, to make the Service auto-connectable again.
service_->SetState(Service::kStateIdle);
EXPECT_TRUE(IsAutoConnectable(&reason));
// The following test cases are copied from ServiceTest.IsAutoConnectable
service_->SetConnectable(true);
EXPECT_TRUE(IsAutoConnectable(&reason));
// We should not auto-connect to a Service that a user has
// deliberately disconnected.
Error error;
service_->UserInitiatedDisconnect("RPC", &error);
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
// If the Service is reloaded, it is eligible for auto-connect again.
EXPECT_TRUE(service_->Load(&storage_));
EXPECT_TRUE(IsAutoConnectable(&reason));
// A non-user initiated Disconnect doesn't change anything.
service_->Disconnect(&error, "in test");
EXPECT_TRUE(IsAutoConnectable(&reason));
// A resume also re-enables auto-connect.
service_->UserInitiatedDisconnect("RPC", &error);
EXPECT_FALSE(IsAutoConnectable(&reason));
service_->OnAfterResume();
EXPECT_TRUE(IsAutoConnectable(&reason));
service_->SetState(Service::kStateConnected);
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnConnected, reason);
service_->SetState(Service::kStateAssociating);
EXPECT_FALSE(IsAutoConnectable(&reason));
EXPECT_STREQ(Service::kAutoConnConnecting, reason);
}
TEST_F(CellularServiceTest, LoadResetsPPPAuthFailure) {
const string kDefaultUser;
const string kDefaultPass;
const string kNewUser("new-username");
const string kNewPass("new-password");
for (const auto change_username : {false, true}) {
for (const auto change_password : {false, true}) {
service_->ppp_username_ = kDefaultUser;
service_->ppp_password_ = kDefaultPass;
service_->SetFailure(Service::kFailurePPPAuth);
EXPECT_TRUE(service_->IsFailed());
EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
if (change_username) {
storage_.SetString(storage_id_, CellularService::kStoragePPPUsername,
kNewUser);
}
if (change_password) {
storage_.SetString(storage_id_, CellularService::kStoragePPPPassword,
kNewPass);
}
EXPECT_TRUE(service_->Load(&storage_));
if (change_username || change_password) {
EXPECT_NE(Service::kFailurePPPAuth, service_->failure());
} else {
EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
}
}
}
}
// The default storage_identifier_ will be {kCellular}_{kIccid}, however older
// profile/storage entries may use a different identifier. This sets up an entry
// with a matching ICCID but an arbitrary storage id and ensures that the older
// storage_identifer_ value is set.
TEST_F(CellularServiceTest, LoadFromProfileMatchingIccid) {
string initial_storage_id = storage_id_;
string matching_storage_id = "another-storage-id";
storage_.DeleteGroup(initial_storage_id);
storage_.SetString(matching_storage_id, CellularService::kStorageType,
kTypeCellular);
storage_.SetString(matching_storage_id, CellularService::kStorageIccid,
kIccid);
storage_.SetString(matching_storage_id, CellularService::kStorageImsi, kImsi);
EXPECT_TRUE(service_->IsLoadableFrom(storage_));
EXPECT_TRUE(service_->Load(&storage_));
EXPECT_EQ(matching_storage_id, service_->GetStorageIdentifier());
}
TEST_F(CellularServiceTest, LoadFromFirstOfMultipleMatchingProfiles) {
string initial_storage_id = storage_id_;
string matching_storage_ids[] = {"another-storage-id1", "another-storage-id2",
"another-storage-id3"};
storage_.DeleteGroup(initial_storage_id);
for (auto& matching_storage_id : matching_storage_ids) {
storage_.SetString(matching_storage_id, CellularService::kStorageType,
kTypeCellular);
storage_.SetString(matching_storage_id, CellularService::kStorageIccid,
kIccid);
storage_.SetString(matching_storage_id, CellularService::kStorageImsi,
kImsi);
}
EXPECT_TRUE(service_->IsLoadableFrom(storage_));
EXPECT_TRUE(service_->Load(&storage_));
EXPECT_EQ(matching_storage_ids[0], service_->GetStorageIdentifier());
}
TEST_F(CellularServiceTest, Save) {
EXPECT_TRUE(service_->Save(&storage_));
std::string iccid;
EXPECT_TRUE(
storage_.GetString(storage_id_, CellularService::kStorageIccid, &iccid));
EXPECT_EQ(iccid, device_->iccid());
}
TEST_F(CellularServiceTest, SaveAndLoadApn) {
static const char kApn[] = "petal.net";
static const char kUsername[] = "orekid";
static const char kPassword[] = "arlet";
static const char kAuthentication[] = "chap";
Error error;
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
testapn[kApnPasswordProperty] = kPassword;
testapn[kApnAuthenticationProperty] = kAuthentication;
service_->SetApn(testapn, &error);
ASSERT_TRUE(error.IsSuccess());
EXPECT_TRUE(service_->Save(&storage_));
// Clear the APN, and then load it from storage again.
Stringmap emptyapn;
service_->SetApn(emptyapn, &error);
ASSERT_TRUE(error.IsSuccess());
EXPECT_TRUE(service_->Load(&storage_));
Stringmap resultapn = service_->GetApn(&error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kApn, resultapn[kApnProperty]);
EXPECT_EQ(kUsername, resultapn[kApnUsernameProperty]);
EXPECT_EQ(kPassword, resultapn[kApnPasswordProperty]);
EXPECT_EQ(kAuthentication, resultapn[kApnAuthenticationProperty]);
}
TEST_F(CellularServiceTest, IgnoreUnversionedLastGoodApn) {
static const char kApn[] = "petal.net";
static const char kUsername[] = "orekid";
Stringmap testapn;
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
service_->SetLastGoodApn(testapn);
ASSERT_TRUE(service_->Save(&storage_));
// Now clear the LastGoodAPN and try to load it. It should be ignored.
service_->ClearLastGoodApn();
ASSERT_TRUE(service_->Load(&storage_));
Stringmap* resultapn = service_->GetLastGoodApn();
EXPECT_EQ(nullptr, resultapn);
}
TEST_F(CellularServiceTest, MergeDetailsFromApnList) {
static const char kApn[] = "petal.net";
static const char kUsername[] = "orekid";
static const char kPassword[] = "arlet";
static const char kAuthentication[] = "chap";
Stringmap fullapn;
fullapn[kApnProperty] = kApn;
fullapn[kApnUsernameProperty] = kUsername;
fullapn[kApnPasswordProperty] = kPassword;
fullapn[kApnAuthenticationProperty] = kAuthentication;
Stringmaps apn_list{fullapn};
device_->set_apn_list(apn_list);
// Just set an APN with only the name. Check that we are using
// the rest of the details.
Error error;
Stringmap testapn;
testapn[kApnProperty] = kApn;
service_->SetApn(testapn, &error);
Stringmap resultapn = service_->GetApn(&error);
EXPECT_TRUE(error.IsSuccess());
EXPECT_EQ(kApn, resultapn[kApnProperty]);
EXPECT_EQ(kUsername, resultapn[kApnUsernameProperty]);
EXPECT_EQ(kPassword, resultapn[kApnPasswordProperty]);
EXPECT_EQ(kAuthentication, resultapn[kApnAuthenticationProperty]);
}
// Some of these tests duplicate signals tested above. However, it's
// convenient to have all the property change notifications documented
// (and tested) in one place.
TEST_F(CellularServiceTest, PropertyChanges) {
TestCommonPropertyChanges(service_, adaptor_);
TestAutoConnectPropertyChange(service_, adaptor_);
EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationTypeProperty, _));
service_->SetActivationType(CellularService::kActivationTypeOTA);
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_NE(kActivationStateNotActivated, service_->activation_state());
EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _));
service_->SetActivationState(kActivationStateNotActivated);
Mock::VerifyAndClearExpectations(adaptor_);
string network_technology = service_->network_technology();
EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty, _));
service_->SetNetworkTechnology(network_technology + "and some new stuff");
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_CALL(*adaptor_, EmitBoolChanged(kOutOfCreditsProperty, true));
service_->NotifySubscriptionStateChanged(SubscriptionState::kOutOfCredits);
Mock::VerifyAndClearExpectations(adaptor_);
EXPECT_CALL(*adaptor_, EmitBoolChanged(kOutOfCreditsProperty, false));
service_->NotifySubscriptionStateChanged(SubscriptionState::kProvisioned);
Mock::VerifyAndClearExpectations(adaptor_);
string roaming_state = service_->roaming_state();
EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty, _));
service_->SetRoamingState(roaming_state + "and some new stuff");
Mock::VerifyAndClearExpectations(adaptor_);
}
// Custom property setters should return false, and make no changes, if
// the new value is the same as the old value.
TEST_F(CellularServiceTest, CustomSetterNoopChange) {
// Test that we didn't break any setters provided by the base class.
TestCustomSetterNoopChange(service_, &manager_);
// Test the new setter we added.
// First set up our environment...
static const char kApn[] = "TheAPN";
static const char kUsername[] = "commander.data";
Error error;
Stringmap testapn;
ProfileRefPtr profile(new NiceMock<MockProfile>(nullptr));
service_->set_profile(profile);
testapn[kApnProperty] = kApn;
testapn[kApnUsernameProperty] = kUsername;
// ... then set to a known value ...
EXPECT_TRUE(service_->SetApn(testapn, &error));
EXPECT_TRUE(error.IsSuccess());
// ... then set to same value.
EXPECT_FALSE(service_->SetApn(testapn, &error));
EXPECT_TRUE(error.IsSuccess());
}
TEST_F(CellularServiceTest, IsMeteredByDefault) {
// These services should be metered by default.
EXPECT_TRUE(service_->IsMetered());
}
TEST_F(CellularServiceTest, SetActivationState) {
// SetActivationState should emit a change.
EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty,
kActivationStateNotActivated));
service_->SetActivationState(kActivationStateNotActivated);
EXPECT_EQ(service_->activation_state(), kActivationStateNotActivated);
EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _))
.Times(AnyNumber());
// Setting the activation state to activated should also set AutoConnect.
EXPECT_FALSE(service_->auto_connect());
service_->SetActivationState(kActivationStateActivated);
EXPECT_EQ(service_->activation_state(), kActivationStateActivated);
EXPECT_TRUE(service_->auto_connect());
// After a client sets AutoConnect to false, setting the activation state to
// activated should not set AutoConnect.
SetAutoConnectFull(false);
EXPECT_FALSE(service_->auto_connect());
service_->SetActivationState(kActivationStateNotActivated);
EXPECT_EQ(service_->activation_state(), kActivationStateNotActivated);
EXPECT_FALSE(service_->auto_connect());
service_->SetActivationState(kActivationStateActivated);
EXPECT_EQ(service_->activation_state(), kActivationStateActivated);
EXPECT_FALSE(service_->auto_connect());
}
} // namespace shill