blob: 923cf3fa8528b2134c38fbb30f1cfda7859879c7 [file] [log] [blame]
// Copyright 2020 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_provider.h"
#include <set>
#include <string>
#include <vector>
#include "shill/cellular/cellular_service.h"
#include "shill/logging.h"
#include "shill/manager.h"
#include "shill/store_interface.h"
#include <base/check.h>
#include <base/check_op.h>
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kCellular;
static std::string ObjectID(const CellularServiceProvider* e) {
return "(cellular_service_provider)";
}
} // 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::kNotSupported,
"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::kNotSupported,
"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(this, 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(this, 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) for a new service (i.e without
// an existing GUID). For Cellular, this should never happen.
Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
"Only existing Cellular services can be configured.");
return nullptr;
}
ServiceRefPtr CellularServiceProvider::GetService(const KeyValueStore& args,
Error* error) {
SLOG(this, 2) << __func__;
// This is called from Manager::GetService or Manager::ConfigureService when
// the corresponding Manager dbus api call is made (e.g. from Chrome) for a
// new service (i.e without an existing GUID). For Cellular, this should never
// happen.
Error::PopulateAndLog(
FROM_HERE, error, Error::kNotSupported,
"GetService must be called with an existing Cellular Service GUID.");
return nullptr;
}
ServiceRefPtr CellularServiceProvider::CreateTemporaryService(
const KeyValueStore& args, Error* error) {
SLOG(this, 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(this, 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(this, 2) << __func__;
}
void CellularServiceProvider::Stop() {
SLOG(this, 2) << __func__;
while (!services_.empty())
RemoveService(services_.back());
}
CellularServiceRefPtr CellularServiceProvider::LoadServicesForDevice(
Cellular* device) {
SLOG(this, 2) << __func__ << ": " << 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(this, 2) << __func__ << ": " << device->iccid();
std::vector<CellularServiceRefPtr> services_to_remove;
for (CellularServiceRefPtr& service : services_) {
if (!device->HasSimCardId(service->GetSimCardId()))
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);
std::string sim_card_id = eid.empty() ? iccid : eid;
args.Set<std::string>(CellularService::kStorageSimCardId, sim_card_id);
std::set<std::string> groups = storage->GetGroupsWithProperties(args);
LOG(INFO) << __func__ << ": " << sim_card_id << ": 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: " << sim_card_id
<< ", removing old or invalid profile entry.";
storage->DeleteGroup(group);
continue;
}
DCHECK_EQ(service_eid, eid);
CellularServiceRefPtr service = FindService(service_iccid);
if (!service) {
SLOG(this, 1) << "Creating Cellular service for ICCID: " << service_iccid;
service = new CellularService(manager_, service_imsi, service_iccid,
service_eid);
service->Load(storage);
service->SetDevice(device);
AddService(service);
} else {
SLOG(this, 2) << "Cellular service exists for ICCID: " << service_iccid;
service->SetDevice(device);
}
if (service_iccid == iccid)
active_service = service;
}
// Ensure that a Service exists for the ICCID.
if (!active_service) {
SLOG(this, 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(this, 1) << __func__ << " eid: " << eid << " iccid: " << iccid;
LoadMatchingServicesFromProfile(eid, iccid, imsi, device);
}
void CellularServiceProvider::UpdateServices(Cellular* device) {
SLOG(this, 2) << __func__;
for (CellularServiceRefPtr& service : services_)
service->SetDevice(device);
}
void CellularServiceProvider::RemoveServices() {
SLOG(this, 1) << __func__;
while (!services_.empty())
RemoveService(services_.back());
}
CellularServiceRefPtr CellularServiceProvider::FindService(
const std::string& iccid) {
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;
}
bool CellularServiceProvider::OnServiceUnloaded(
const CellularServiceRefPtr& service) {
SLOG(this, 1) << __func__ << ": " << service->iccid();
const CellularRefPtr device = service->cellular();
if (device && device->iccid() == service->iccid()) {
LOG(ERROR) << "Service with active ICCID unloaded: " << service->iccid();
return false;
}
auto iter = std::find(services_.begin(), services_.end(), service);
if (iter != services_.end())
services_.erase(iter);
return true;
}
void CellularServiceProvider::AddService(CellularServiceRefPtr service) {
SLOG(this, 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(this, 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);
}
} // namespace shill