blob: 88f6a90f480a38bcbf254a1ad2d0cee88e0d6cec [file] [log] [blame] [edit]
// Copyright 2020 The ChromiumOS Authors
// 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_provider.h"
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include "shill/cellular/cellular_service.h"
#include "shill/logging.h"
#include "shill/manager.h"
#include "shill/store/store_interface.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kCellular;
} // namespace Logging
namespace {
bool IsValidEid(const std::string& sim_card_id) {
// eID must be 32 characters in length. Since ICCID is limited to 20
// characters, this is a strong indicator of a valid eID.
return sim_card_id.size() == 32;
}
bool GetServiceParametersFromArgs(const KeyValueStore& args,
std::string* imsi,
std::string* iccid,
std::string* eid,
Error* error) {
*iccid =
args.Lookup<std::string>(CellularService::kStorageIccid, std::string());
if (iccid->empty()) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
"Missing ICCID");
return false;
}
// If SimCardId != ICCID, it matches the eID. TODO(b/182943364): Store eID.
std::string sim_card_id = args.Lookup<std::string>(
CellularService::kStorageSimCardId, std::string());
if (sim_card_id != *iccid) {
if (IsValidEid(sim_card_id)) {
*eid = sim_card_id;
} else {
LOG(ERROR) << "Unexpected SIM Card Id: " << sim_card_id;
*eid = "";
}
} else {
*eid = "";
}
// IMSI may be empty.
*imsi =
args.Lookup<std::string>(CellularService::kStorageImsi, std::string());
return true;
}
bool GetServiceParametersFromStorage(const StoreInterface* storage,
const std::string& entry_name,
std::string* imsi,
std::string* iccid,
std::string* eid,
Error* error) {
if (!storage->GetString(entry_name, CellularService::kStorageIccid, iccid) ||
iccid->empty()) {
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidProperty,
"Missing or empty ICCID");
return false;
}
// If SimCardId != ICCID, it matches the eID. TODO(b/182943364): Store eID.
std::string sim_card_id;
if (storage->GetString(entry_name, CellularService::kStorageSimCardId,
&sim_card_id) &&
sim_card_id != *iccid) {
if (IsValidEid(sim_card_id)) {
*eid = sim_card_id;
} else {
LOG(ERROR) << "Unexpected SIM Card Id: " << sim_card_id;
*eid = "";
}
} else {
*eid = "";
}
// IMSI may be empty.
storage->GetString(entry_name, CellularService::kStorageImsi, imsi);
return true;
}
} // namespace
CellularServiceProvider::CellularServiceProvider(Manager* manager)
: manager_(manager) {}
CellularServiceProvider::~CellularServiceProvider() = default;
void CellularServiceProvider::CreateServicesFromProfile(
const ProfileRefPtr& profile) {
SLOG(2) << __func__ << ": " << profile->GetFriendlyName();
// A Cellular Device may not exist yet, so we do not load services here.
// Cellular services associated with a Device are loaded in
// LoadServicesForDevice when the Device is created. We store |profile| here
// so that we always use the default profile (see comment in header).
if (!profile_)
profile_ = profile;
}
ServiceRefPtr CellularServiceProvider::FindSimilarService(
const KeyValueStore& args, Error* error) const {
SLOG(2) << __func__;
CHECK_EQ(kTypeCellular, args.Lookup<std::string>(kTypeProperty, ""))
<< "Service type must be Cellular!";
// This is called from Manager::ConfigureServiceForProfile when the Manager
// dbus api call is made (e.g. from Chrome). When a Cellular Service is
// configured (e.g. from policy), find any existing Service matching |iccid|
// and update that configuration.
std::string iccid = args.Lookup<std::string>(kIccidProperty,
/*default_value=*/"");
return FindService(iccid);
}
ServiceRefPtr CellularServiceProvider::GetService(const KeyValueStore& args,
Error* error) {
SLOG(2) << __func__;
// This is called from Manager::GetService or Manager::ConfigureService when
// the corresponding Manager dbus api call is made (e.g. from Chrome). When a
// Cellular Service is configured (e.g. from policy), find any existing
// Service matching |iccid| and update that configuration. If there's no
// matching Service, a new Cellular Service is created with the given ICCID
// and EID from |args|.
std::string iccid = args.Lookup<std::string>(kIccidProperty,
/*default_value=*/"");
CellularServiceRefPtr service = FindService(iccid);
if (service)
return service;
std::string eid = args.Lookup<std::string>(kEidProperty,
/*default_value=*/"");
LOG(INFO) << "Creating new cellular service with iccid: " << iccid
<< ", eid: " << eid;
service = new CellularService(manager_, "", iccid, eid);
AddService(service);
return service;
}
ServiceRefPtr CellularServiceProvider::CreateTemporaryService(
const KeyValueStore& args, Error* error) {
SLOG(2) << __func__;
std::string imsi, iccid, eid;
if (GetServiceParametersFromArgs(args, &imsi, &iccid, &eid, error)) {
return new CellularService(manager_, imsi, iccid, eid);
}
return nullptr;
}
ServiceRefPtr CellularServiceProvider::CreateTemporaryServiceFromProfile(
const ProfileRefPtr& profile, const std::string& entry_name, Error* error) {
SLOG(2) << __func__ << ": " << profile->GetFriendlyName();
std::string imsi, iccid, eid;
if (GetServiceParametersFromStorage(profile->GetConstStorage(), entry_name,
&imsi, &iccid, &eid, error)) {
return new CellularService(manager_, imsi, iccid, eid);
}
return nullptr;
}
void CellularServiceProvider::Start() {
SLOG(2) << __func__;
}
void CellularServiceProvider::Stop() {
SLOG(2) << __func__;
RemoveServices();
}
CellularServiceRefPtr CellularServiceProvider::LoadServicesForDevice(
Cellular* device) {
SLOG(2) << __func__ << " Device ICCID: " << device->iccid();
CellularServiceRefPtr active_service = LoadMatchingServicesFromProfile(
device->eid(), device->iccid(), device->imsi(), device);
// When the Cellular SIM changes or Cellular is enabled, assume that the
// intent is to auto connect to the CellularService (if connectable and
// AutoConnect are set), even if the service was previously explicitly
// disconnected.
active_service->ClearExplicitlyDisconnected();
return active_service;
}
void CellularServiceProvider::RemoveNonDeviceServices(Cellular* device) {
SLOG(2) << __func__ << " Device ICCID: " << device->iccid();
std::vector<CellularServiceRefPtr> services_to_remove;
for (CellularServiceRefPtr& service : services_) {
if (!device->HasIccid(service->iccid()))
services_to_remove.push_back(service);
}
for (CellularServiceRefPtr& service : services_to_remove)
RemoveService(service);
}
CellularServiceRefPtr CellularServiceProvider::LoadMatchingServicesFromProfile(
const std::string& eid,
const std::string& iccid,
const std::string& imsi,
Cellular* device) {
DCHECK(device);
// Find Cellular profile entries matching the sim card identifier.
DCHECK(profile_);
StoreInterface* storage = profile_->GetStorage();
DCHECK(storage);
KeyValueStore args;
args.Set<std::string>(kTypeProperty, kTypeCellular);
args.Set<std::string>(CellularService::kStorageIccid, iccid);
std::set<std::string> groups = storage->GetGroupsWithProperties(args);
SLOG(2) << __func__ << ": " << iccid;
LOG(INFO) << __func__ << ": Groups: " << groups.size();
CellularServiceRefPtr active_service = nullptr;
for (const std::string& group : groups) {
std::string service_imsi, service_iccid, service_eid;
if (!GetServiceParametersFromStorage(storage, group, &service_imsi,
&service_iccid, &service_eid,
/*error=*/nullptr)) {
LOG(ERROR) << "Unable to load service properties for: " << iccid
<< ", removing old or invalid profile entry.";
storage->DeleteGroup(group);
continue;
}
DCHECK_EQ(service_eid, eid);
CellularServiceRefPtr service = FindService(service_iccid);
if (!service) {
SLOG(1) << "Creating Cellular service for ICCID: " << service_iccid;
service = new CellularService(manager_, service_imsi, service_iccid,
service_eid);
// Device.AllowRoaming was used to store roaming preferences before M94.
// To honor settings for services created before M94, we default
// Service.AllowRoaming to the value of Device.AllowRoaming.
// If a value for Service.AllowRoaming was persisted when the service was
// last used, the default is overridden in Service::Load,
// else the default value is stored to disk during AddService, thus the
// value of Device.AllowRoaming is copied over to the service. This
// completes the migration of Device.AllowRoaming to Service.AllowRoaming.
// The plan is to remove references to device->allow_roaming_ in M108,
// when we assume all services created before M94 have been used at least
// once between M94 and M108, and thus have migrated their AllowRoaming.
service->set_allow_roaming(device->allow_roaming());
service->Load(storage);
service->SetDevice(device);
AddService(service);
} else {
SLOG(2) << "Cellular service exists for ICCID: " << service_iccid;
service->SetDevice(device);
}
if (service_iccid == iccid)
active_service = service;
}
if (active_service)
return active_service;
// If a Service was never saved, it may still exist in |services_|.
active_service = FindService(iccid);
if (active_service) {
SLOG(2) << "Cellular service exists for ICCID: " << iccid
<< " (but not saved)";
active_service->SetDevice(device);
return active_service;
}
// Create a Service for the ICCID.
SLOG(1) << "No existing Cellular service with ICCID: " << iccid;
active_service = new CellularService(manager_, imsi, iccid, eid);
active_service->SetDevice(device);
AddService(active_service);
return active_service;
}
void CellularServiceProvider::LoadServicesForSecondarySim(
const std::string& eid,
const std::string& iccid,
const std::string& imsi,
Cellular* device) {
DCHECK(!iccid.empty());
SLOG(1) << __func__ << " eid: " << eid << " iccid: " << iccid;
LoadMatchingServicesFromProfile(eid, iccid, imsi, device);
}
void CellularServiceProvider::UpdateServices(Cellular* device) {
SLOG(2) << __func__;
for (CellularServiceRefPtr& service : services_)
service->SetDevice(device);
}
void CellularServiceProvider::RemoveServices() {
SLOG(1) << __func__;
while (!services_.empty())
RemoveService(services_.back());
}
CellularServiceRefPtr CellularServiceProvider::FindService(
const std::string& iccid) const {
const auto iter = std::find_if(
services_.begin(), services_.end(),
[iccid](const auto& service) { return service->iccid() == iccid; });
if (iter != services_.end())
return *iter;
return nullptr;
}
void CellularServiceProvider::AddService(CellularServiceRefPtr service) {
SLOG(1) << __func__ << " with ICCID: " << service->iccid();
// See comment in header for |profile_|.
service->SetProfile(profile_);
// Save any changes to device properties (iccid, eid).
profile_->UpdateService(service);
manager_->RegisterService(service);
services_.push_back(service);
}
void CellularServiceProvider::RemoveService(CellularServiceRefPtr service) {
SLOG(1) << __func__ << " with ICCID: " << service->iccid();
manager_->DeregisterService(service);
auto iter = std::find(services_.begin(), services_.end(), service);
if (iter == services_.end()) {
LOG(ERROR) << "RemoveService: Not found: ";
return;
}
services_.erase(iter);
}
CellularService* CellularServiceProvider::GetActiveService() {
for (CellularServiceRefPtr& service : services_) {
if (service->IsActive(nullptr))
return service.get();
}
return nullptr;
}
void CellularServiceProvider::TetheringEntitlementCheck(
base::OnceCallback<void(TetheringManager::EntitlementStatus)> callback) {
SLOG(3) << __func__;
const auto cellular_service = GetActiveService();
if (!cellular_service || !cellular_service->cellular()) {
SLOG(3) << __func__ << " cellular device doesn't exist";
manager_->metrics()->NotifyCellularEntitlementCheckResult(
Metrics::kCellularEntitlementCheckNoCellularDevice);
std::move(callback).Run(
TetheringManager::EntitlementStatus::kUpstreamNetworkNotAvailable);
return;
}
cellular_service->cellular()->EntitlementCheck(std::move(callback));
}
void CellularServiceProvider::AcquireTetheringNetwork(
base::OnceCallback<void(TetheringManager::SetEnabledResult, Network*)>
callback) {
SLOG(3) << __func__;
// For now assume that the main data network is always used for tethering.
// TODO(b/249151422) Implement tethering network selection logic and supports:
// - switching the main data connection to a different APN for tethering,
// - or bringing up a new separate APN connection only for tethering.
manager_->dispatcher()->PostTask(
FROM_HERE,
base::BindOnce(&CellularServiceProvider::OnTetheringNetworkReady,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CellularServiceProvider::OnTetheringNetworkReady(
base::OnceCallback<void(TetheringManager::SetEnabledResult, Network*)>
callback) {
SLOG(3) << __func__;
// Even when reusing the main data connection, make sure that the Network
// object passed back to the caller is obtained at same time as when the
// caller's callback is triggered and no sooner. This avoid returning a
// pointer that could be invalidated by the time the callback is triggered
// (network disconnection).
const auto cellular_service = GetActiveService();
if (!cellular_service || !cellular_service->cellular()) {
std::move(callback).Run(
TetheringManager::SetEnabledResult::kUpstreamNetworkNotAvailable,
nullptr);
return;
}
const auto network = cellular_service->attached_network();
if (!network || !network->IsConnected()) {
std::move(callback).Run(
TetheringManager::SetEnabledResult::kUpstreamNetworkNotAvailable,
nullptr);
return;
}
std::move(callback).Run(TetheringManager::SetEnabledResult::kSuccess,
network);
}
void CellularServiceProvider::ReleaseTetheringNetwork(
Network* network, base::OnceCallback<void(bool is_success)> callback) {
SLOG(3) << __func__;
// Always assume for now that the tethering upstream network was the main data
// network, i.e there is nothing to do.
// TODO(b/249151422) Switch back to original data APN if the main data network
// was switched to a different APN for tethering.
// TODO(b/249151422) Tear down the tethering APN if a separate APN connection
// was brought up for tethering.
manager_->dispatcher()->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), true));
}
} // namespace shill