// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/dbus/hermes/fake_hermes_euicc_client.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
#include "chromeos/ash/components/dbus/shill/fake_shill_service_client.h"
#include "chromeos/ash/components/dbus/shill/shill_device_client.h"
#include "chromeos/ash/components/dbus/shill/shill_profile_client.h"
#include "chromeos/dbus/constants/dbus_switches.h"
#include "dbus/object_path.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace ash {
namespace {
const char* kDefaultMccMnc = "310999";
const char* kFakeActivationCodePrefix = "LPA:1$SMDP.GSMA.COM$";
const char* kActivationCodeToTriggerDBusError = "no_memory";
const char* kFakeProfilePathPrefix = "/org/chromium/Hermes/Profile/";
const char* kFakeIccidPrefix = "10000000000000000";
const char* kFakeProfileNamePrefix = "FakeCellularNetwork_";
const char* kFakeProfileNicknamePrefix = "FakeCellularNetworkNickname_";
const char* kFakeServiceProvider = "Fake Wireless";
const char* kFakeNetworkServicePathPrefix = "/service/cellular1";
bool PopPendingProfile(HermesEuiccClient::Properties* properties,
dbus::ObjectPath carrier_profile_path) {
std::vector<dbus::ObjectPath> profiles = properties->profiles().value();
auto it = base::ranges::find(profiles, carrier_profile_path);
if (it == profiles.end()) {
return false;
}
HermesProfileClient::Properties* profile_properties =
HermesProfileClient::Get()->GetProperties(*it);
if (profile_properties->state().value() != hermes::profile::State::kPending) {
return false;
}
profiles.erase(it);
properties->profiles().ReplaceValue(profiles);
return true;
}
dbus::ObjectPath PopPendingProfileWithActivationCode(
HermesEuiccClient::Properties* euicc_properties,
const std::string& activation_code) {
std::vector<dbus::ObjectPath> profiles = euicc_properties->profiles().value();
for (auto it = profiles.begin(); it != profiles.end(); it++) {
dbus::ObjectPath carrier_profile_path = *it;
HermesProfileClient::Properties* profile_properties =
HermesProfileClient::Get()->GetProperties(carrier_profile_path);
if (profile_properties->activation_code().value() == activation_code) {
profiles.erase(it);
euicc_properties->profiles().ReplaceValue(profiles);
return carrier_profile_path;
}
}
return dbus::ObjectPath();
}
base::Value::List ExtractPSimSlotInfo(const base::Value* sim_slot_info_list) {
base::Value::List psim_slot_info_list;
if (!sim_slot_info_list || !sim_slot_info_list->is_list()) {
return psim_slot_info_list;
}
for (const auto& sim_slot_info : *sim_slot_info_list->GetIfList()) {
if (!sim_slot_info.is_dict()) {
continue;
}
const std::string* eid =
sim_slot_info.GetIfDict()->FindString(shill::kSIMSlotInfoEID);
if (!eid || eid->empty()) {
psim_slot_info_list.Append(sim_slot_info.Clone());
}
}
return psim_slot_info_list;
}
// The `index` is used when formatting the activation code and is intended to
// ensure that all fake activation codes are unique.
std::string GenerateFakeActivationCodeWithIndex(int index) {
return base::StringPrintf("%s%010d", kFakeActivationCodePrefix, index);
}
} // namespace
FakeHermesEuiccClient::Properties::Properties(
const PropertyChangedCallback& callback)
: HermesEuiccClient::Properties(nullptr, callback) {}
FakeHermesEuiccClient::Properties::~Properties() = default;
void FakeHermesEuiccClient::Properties::Get(
dbus::PropertyBase* property,
dbus::PropertySet::GetCallback callback) {
DVLOG(1) << "Get " << property->name();
std::move(callback).Run(false);
}
void FakeHermesEuiccClient::Properties::GetAll() {
DVLOG(1) << "GetAll";
}
void FakeHermesEuiccClient::Properties::Set(
dbus::PropertyBase* property,
dbus::PropertySet::SetCallback callback) {
DVLOG(1) << "Set " << property->name();
std::move(callback).Run(false);
}
FakeHermesEuiccClient::FakeHermesEuiccClient() = default;
FakeHermesEuiccClient::~FakeHermesEuiccClient() = default;
void FakeHermesEuiccClient::ClearEuicc(const dbus::ObjectPath& euicc_path) {
PropertiesMap::iterator it = properties_map_.find(euicc_path);
if (it == properties_map_.end())
return;
auto* profile_test = HermesProfileClient::Get()->GetTestInterface();
HermesEuiccClient::Properties* properties = it->second.get();
for (const auto& path : properties->profiles().value()) {
profile_test->ClearProfile(path);
}
properties_map_.erase(it);
}
void FakeHermesEuiccClient::ResetPendingEventsRequested() {
pending_event_requested_ = false;
}
dbus::ObjectPath FakeHermesEuiccClient::AddFakeCarrierProfile(
const dbus::ObjectPath& euicc_path,
hermes::profile::State state,
const std::string& activation_code,
AddCarrierProfileBehavior add_carrier_profile_behavior) {
int index = fake_profile_counter_++;
dbus::ObjectPath carrier_profile_path(
base::StringPrintf("%s%02d", kFakeProfilePathPrefix, index));
AddCarrierProfile(
carrier_profile_path, euicc_path,
base::StringPrintf("%s%02d", kFakeIccidPrefix, index),
base::StringPrintf("%s%02d", kFakeProfileNicknamePrefix, index),
base::StringPrintf("%s%02d", kFakeProfileNamePrefix, index),
kFakeServiceProvider,
activation_code.empty() ? GenerateFakeActivationCodeWithIndex(index)
: activation_code,
base::StringPrintf("%s%02d", kFakeNetworkServicePathPrefix, index), state,
hermes::profile::ProfileClass::kOperational,
add_carrier_profile_behavior);
return carrier_profile_path;
}
void FakeHermesEuiccClient::AddCarrierProfile(
const dbus::ObjectPath& path,
const dbus::ObjectPath& euicc_path,
const std::string& iccid,
const std::string& name,
const std::string& nickname,
const std::string& service_provider,
const std::string& activation_code,
const std::string& network_service_path,
hermes::profile::State state,
hermes::profile::ProfileClass profile_class,
AddCarrierProfileBehavior add_carrier_profile_behavior) {
DVLOG(1) << "Adding new profile path=" << path.value() << ", name=" << name
<< ", state=" << state;
HermesProfileClient::Properties* profile_properties =
HermesProfileClient::Get()->GetProperties(path);
profile_properties->iccid().ReplaceValue(iccid);
profile_properties->service_provider().ReplaceValue(service_provider);
profile_properties->mcc_mnc().ReplaceValue(kDefaultMccMnc);
profile_properties->activation_code().ReplaceValue(activation_code);
profile_properties->name().ReplaceValue(name);
profile_properties->nick_name().ReplaceValue(nickname);
profile_properties->state().ReplaceValue(state);
profile_properties->profile_class().ReplaceValue(profile_class);
profile_service_path_map_[path] = network_service_path;
Properties* euicc_properties = GetProperties(euicc_path);
if (state == hermes::profile::State::kPending) {
std::vector<dbus::ObjectPath> profiles =
euicc_properties->profiles().value();
profiles.push_back(path);
euicc_properties->profiles().ReplaceValue(profiles);
return;
}
bool should_create_service =
add_carrier_profile_behavior ==
AddCarrierProfileBehavior::kAddProfileWithService ||
add_carrier_profile_behavior ==
AddCarrierProfileBehavior::kAddDelayedProfileWithService;
if (should_create_service)
CreateCellularService(euicc_path, path);
bool should_delay_install =
add_carrier_profile_behavior ==
AddCarrierProfileBehavior::kAddDelayedProfileWithService ||
add_carrier_profile_behavior ==
AddCarrierProfileBehavior::kAddDelayedProfileWithoutService;
if (should_delay_install) {
QueueInstalledProfile(euicc_path, path);
return;
}
std::vector<dbus::ObjectPath> profiles = euicc_properties->profiles().value();
profiles.push_back(path);
euicc_properties->profiles().ReplaceValue(profiles);
}
bool FakeHermesEuiccClient::RemoveCarrierProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path) {
// Remove entry from profile service path map.
auto profile_service_path_map_iter =
profile_service_path_map_.find(carrier_profile_path);
if (profile_service_path_map_iter == profile_service_path_map_.end()) {
return false;
}
profile_service_path_map_.erase(profile_service_path_map_iter);
// Remove profile from Euicc properties.
Properties* euicc_properties = GetProperties(euicc_path);
std::vector<dbus::ObjectPath> profiles = euicc_properties->profiles().value();
auto profiles_iter = base::ranges::find(profiles, carrier_profile_path);
if (profiles_iter == profiles.end()) {
return false;
}
profiles.erase(profiles_iter);
euicc_properties->profiles().ReplaceValue(profiles);
// Remove profile dbus object.
HermesProfileClient::Get()->GetTestInterface()->ClearProfile(
carrier_profile_path);
return true;
}
void FakeHermesEuiccClient::UpdateShillDeviceSimSlotInfo() {
ShillDeviceClient::TestInterface* device_test =
ShillDeviceClient::Get()->GetTestInterface();
DCHECK(device_test);
const std::string device_path =
device_test->GetDevicePathForType(shill::kTypeCellular);
if (device_path.empty()) {
return;
}
HermesManagerClient* manager_client = HermesManagerClient::Get();
DCHECK(manager_client);
HermesProfileClient* profile_client = HermesProfileClient::Get();
DCHECK(profile_client);
// The list of SIM slot information is expected to be in ascending order by
// the physical slot of the EUICCs.
base::flat_map<int32_t, HermesEuiccClient::Properties*>
physical_slot_to_properties;
for (const auto& euicc_path : manager_client->GetAvailableEuiccs()) {
HermesEuiccClient::Properties* euicc_properties = GetProperties(euicc_path);
DCHECK(euicc_properties);
physical_slot_to_properties.insert_or_assign(
euicc_properties->physical_slot().value(), euicc_properties);
}
// Check if there is already an entry for SIM slot information and copy the
// pSIM information if it is available.
base::Value::List sim_slot_info_list = ExtractPSimSlotInfo(
device_test->GetDeviceProperty(device_path, shill::kSIMSlotInfoProperty));
for (auto [physical_slot, euicc_properties] : physical_slot_to_properties) {
std::string iccid;
for (const auto& profile_path : euicc_properties->profiles().value()) {
HermesProfileClient::Properties* profile_properties =
profile_client->GetProperties(profile_path);
DCHECK(profile_properties);
const dbus::Property<hermes::profile::State>& state =
profile_properties->state();
if (state.value() == hermes::profile::State::kActive) {
iccid = profile_properties->iccid().value();
break;
}
}
base::Value::Dict sim_slot_info;
sim_slot_info.Set(shill::kSIMSlotInfoEID, euicc_properties->eid().value());
sim_slot_info.Set(shill::kSIMSlotInfoICCID, iccid);
sim_slot_info.Set(shill::kSIMSlotInfoPrimary,
euicc_properties->is_active().value());
sim_slot_info_list.Append(std::move(sim_slot_info));
}
device_test->SetDeviceProperty(device_path, shill::kSIMSlotInfoProperty,
base::Value(std::move(sim_slot_info_list)),
/*notify_changed=*/true);
}
void FakeHermesEuiccClient::QueueHermesErrorStatus(
HermesResponseStatus status) {
error_status_queue_.push(status);
}
void FakeHermesEuiccClient::SetNextInstallProfileFromActivationCodeResult(
HermesResponseStatus status) {
CHECK(status != HermesResponseStatus::kSuccess);
next_install_profile_result_ = status;
}
void FakeHermesEuiccClient::SetNextRefreshSmdxProfilesResult(
std::vector<dbus::ObjectPath> profiles) {
next_refresh_smdx_profiles_result_ = std::move(profiles);
}
void FakeHermesEuiccClient::SetInteractiveDelay(base::TimeDelta delay) {
interactive_delay_ = delay;
}
std::string FakeHermesEuiccClient::GenerateFakeActivationCode() {
return GenerateFakeActivationCodeWithIndex(fake_profile_counter_++);
}
std::string FakeHermesEuiccClient::GetDBusErrorActivationCode() {
return kActivationCodeToTriggerDBusError;
}
bool FakeHermesEuiccClient::GetLastRefreshProfilesRestoreSlotArg() {
return last_restore_slot_arg_;
}
void FakeHermesEuiccClient::InstallProfileFromActivationCode(
const dbus::ObjectPath& euicc_path,
const std::string& activation_code,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) {
if (next_install_profile_result_.has_value()) {
std::move(callback).Run(next_install_profile_result_.value(),
dbus::DBusResult::kSuccess,
/*carrier_profile_path=*/nullptr);
next_install_profile_result_ = std::nullopt;
return;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoInstallProfileFromActivationCode,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
activation_code, confirmation_code, std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::InstallPendingProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path,
const std::string& confirmation_code,
HermesResponseCallback callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoInstallPendingProfile,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
carrier_profile_path, confirmation_code,
std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::RefreshInstalledProfiles(
const dbus::ObjectPath& euicc_path,
bool restore_slot,
HermesResponseCallback callback) {
last_restore_slot_arg_ = restore_slot;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoRequestInstalledProfiles,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::RefreshSmdxProfiles(
const dbus::ObjectPath& euicc_path,
const std::string& activation_code,
bool restore_slot,
RefreshSmdxProfilesCallback callback) {
last_restore_slot_arg_ = restore_slot;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoRefreshSmdxProfiles,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
activation_code, std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::RequestPendingProfiles(
const dbus::ObjectPath& euicc_path,
const std::string& root_smds,
HermesResponseCallback callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoRequestPendingProfiles,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::UninstallProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoUninstallProfile,
weak_ptr_factory_.GetWeakPtr(), euicc_path,
carrier_profile_path, std::move(callback)),
interactive_delay_);
}
void FakeHermesEuiccClient::ResetMemory(
const dbus::ObjectPath& euicc_path,
hermes::euicc::ResetOptions reset_option,
HermesResponseCallback callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::DoResetMemory,
weak_ptr_factory_.GetWeakPtr(), euicc_path, reset_option,
std::move(callback)),
interactive_delay_);
}
FakeHermesEuiccClient::Properties* FakeHermesEuiccClient::GetProperties(
const dbus::ObjectPath& euicc_path) {
auto it = properties_map_.find(euicc_path);
if (it != properties_map_.end()) {
return it->second.get();
}
DVLOG(1) << "Creating new Fake Euicc object path =" << euicc_path.value();
std::unique_ptr<Properties> properties(new Properties(
base::BindRepeating(&FakeHermesEuiccClient::CallNotifyPropertyChanged,
weak_ptr_factory_.GetWeakPtr(), euicc_path)));
properties_map_[euicc_path] = std::move(properties);
return properties_map_[euicc_path].get();
}
HermesEuiccClient::TestInterface* FakeHermesEuiccClient::GetTestInterface() {
return this;
}
void FakeHermesEuiccClient::DoInstallProfileFromActivationCode(
const dbus::ObjectPath& euicc_path,
const std::string& activation_code,
const std::string& confirmation_code,
InstallCarrierProfileCallback callback) {
DVLOG(1) << "Installing profile from activation code: code="
<< activation_code << ", confirmation_code=" << confirmation_code;
if (!error_status_queue_.empty()) {
std::move(callback).Run(error_status_queue_.front(),
dbus::DBusResult::kSuccess, nullptr);
error_status_queue_.pop();
return;
}
if (activation_code == kActivationCodeToTriggerDBusError) {
std::move(callback).Run(HermesResponseStatus::kErrorUnknownResponse,
dbus::DBusResult::kErrorNoMemory, nullptr);
return;
}
if (!base::StartsWith(activation_code, kFakeActivationCodePrefix,
base::CompareCase::SENSITIVE)) {
DVLOG(1) << "Unexpected activation code prefix. Fake activation codes "
<< "should begin with '" << kFakeActivationCodePrefix << "'";
std::move(callback).Run(HermesResponseStatus::kErrorInvalidActivationCode,
dbus::DBusResult::kSuccess, nullptr);
return;
}
Properties* euicc_properties = GetProperties(euicc_path);
dbus::ObjectPath profile_path =
PopPendingProfileWithActivationCode(euicc_properties, activation_code);
if (profile_path.IsValid()) {
// Move pending profile to installed.
HermesProfileClient::Properties* profile_properties =
HermesProfileClient::Get()->GetProperties(profile_path);
profile_properties->state().ReplaceValue(hermes::profile::State::kInactive);
std::vector<dbus::ObjectPath> profiles =
euicc_properties->profiles().value();
profiles.push_back(profile_path);
euicc_properties->profiles().ReplaceValue(profiles);
} else {
// Create a new installed profile with given activation code.
profile_path = AddFakeCarrierProfile(
euicc_path, hermes::profile::State::kInactive, activation_code,
AddCarrierProfileBehavior::kAddProfileWithService);
}
CreateCellularService(euicc_path, profile_path);
std::move(callback).Run(HermesResponseStatus::kSuccess,
dbus::DBusResult::kSuccess, &profile_path);
}
void FakeHermesEuiccClient::DoInstallPendingProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path,
const std::string& confirmation_code,
HermesResponseCallback callback) {
DVLOG(1) << "Installing pending profile: path="
<< carrier_profile_path.value()
<< ", confirmation_code=" << confirmation_code;
if (!error_status_queue_.empty()) {
std::move(callback).Run(error_status_queue_.front());
error_status_queue_.pop();
return;
}
Properties* euicc_properties = GetProperties(euicc_path);
if (!PopPendingProfile(euicc_properties, carrier_profile_path)) {
std::move(callback).Run(HermesResponseStatus::kErrorUnknown);
return;
}
HermesProfileClient::Properties* profile_properties =
HermesProfileClient::Get()->GetProperties(carrier_profile_path);
profile_properties->state().ReplaceValue(hermes::profile::State::kInactive);
std::vector<dbus::ObjectPath> profiles = euicc_properties->profiles().value();
profiles.push_back(carrier_profile_path);
euicc_properties->profiles().ReplaceValue(profiles);
CreateCellularService(euicc_path, carrier_profile_path);
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
void FakeHermesEuiccClient::DoRequestInstalledProfiles(
const dbus::ObjectPath& euicc_path,
HermesResponseCallback callback) {
DVLOG(1) << "Installed Profiles Requested";
if (!error_status_queue_.empty()) {
std::move(callback).Run(error_status_queue_.front());
error_status_queue_.pop();
return;
}
auto iter = installed_profile_queue_map_.find(euicc_path);
if (iter != installed_profile_queue_map_.end() && !iter->second->empty()) {
InstalledProfileQueue* installed_profile_queue = iter->second.get();
Properties* euicc_properties = GetProperties(euicc_path);
std::vector<dbus::ObjectPath> profiles =
euicc_properties->profiles().value();
while (!installed_profile_queue->empty()) {
profiles.push_back(installed_profile_queue->front());
installed_profile_queue->pop();
}
euicc_properties->profiles().ReplaceValue(profiles);
}
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
void FakeHermesEuiccClient::DoRefreshSmdxProfiles(
const dbus::ObjectPath& euicc_path,
const std::string& activation_code,
RefreshSmdxProfilesCallback callback) {
DVLOG(1) << "Refresh SM-DX Profiles Requested";
HermesResponseStatus status = HermesResponseStatus::kSuccess;
if (!error_status_queue_.empty()) {
status = error_status_queue_.front();
error_status_queue_.pop();
}
if (next_refresh_smdx_profiles_result_.has_value()) {
std::move(callback).Run(status, next_refresh_smdx_profiles_result_.value());
next_refresh_smdx_profiles_result_ = std::nullopt;
return;
}
std::vector<dbus::ObjectPath> profile_paths;
if (status != HermesResponseStatus::kSuccess) {
std::move(callback).Run(status, profile_paths);
return;
}
Properties* euicc_properties = GetProperties(euicc_path);
DCHECK(euicc_properties);
// Collect all of the existing, pending profiles that have an activation code
// that matches |activation_code| to be returned.
for (const auto& profile_path : euicc_properties->profiles().value()) {
HermesProfileClient::Properties* properties =
HermesProfileClient::Get()->GetProperties(profile_path);
if (properties &&
properties->activation_code().value() == activation_code &&
properties->state().value() == hermes::profile::State::kPending) {
profile_paths.push_back(profile_path);
}
}
// If no pending profiles exist with a matching activation code, create one.
if (profile_paths.empty()) {
profile_paths.push_back(AddFakeCarrierProfile(
euicc_path, hermes::profile::State::kPending, activation_code,
AddCarrierProfileBehavior::kAddProfileWithService));
}
std::move(callback).Run(status, profile_paths);
}
void FakeHermesEuiccClient::DoRequestPendingProfiles(
const dbus::ObjectPath& euicc_path,
HermesResponseCallback callback) {
DVLOG(1) << "Pending Profiles Requested";
if (!error_status_queue_.empty()) {
std::move(callback).Run(error_status_queue_.front());
error_status_queue_.pop();
return;
}
if (!pending_event_requested_) {
AddFakeCarrierProfile(euicc_path, hermes::profile::State::kPending, "",
AddCarrierProfileBehavior::kAddProfileWithService);
pending_event_requested_ = true;
}
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
void FakeHermesEuiccClient::DoUninstallProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path,
HermesResponseCallback callback) {
if (!error_status_queue_.empty()) {
std::move(callback).Run(error_status_queue_.front());
error_status_queue_.pop();
return;
}
// TODO(azeemarshad): Remove Shill service after removing carrier profile.
bool remove_success = RemoveCarrierProfile(euicc_path, carrier_profile_path);
std::move(callback).Run(remove_success
? HermesResponseStatus::kSuccess
: HermesResponseStatus::kErrorInvalidIccid);
}
void FakeHermesEuiccClient::DoResetMemory(
const dbus::ObjectPath& euicc_path,
hermes::euicc::ResetOptions reset_option,
HermesResponseCallback callback) {
HermesEuiccClient::Properties* properties = GetProperties(euicc_path);
while (true) {
const dbus::Property<std::vector<dbus::ObjectPath>>& profiles =
properties->profiles();
if (profiles.value().empty()) {
break;
}
// Use a copy of profile_path since it will be deallocated along with the
// profile.
const dbus::ObjectPath profile_path = profiles.value().front();
bool remove_success = RemoveCarrierProfile(euicc_path, profile_path);
CHECK(remove_success);
}
std::move(callback).Run(HermesResponseStatus::kSuccess);
}
// Creates cellular service in shill for the given carrier profile path.
// This simulates the expected hermes - shill interaction when a new carrier
// profile is installed on the device through Hermes. Shill will be notified and
// it then creates cellular services with matching ICCID for this profile.
void FakeHermesEuiccClient::CreateCellularService(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& carrier_profile_path) {
const std::string& service_path =
profile_service_path_map_[carrier_profile_path];
HermesProfileClient::Properties* properties =
HermesProfileClient::Get()->GetProperties(carrier_profile_path);
HermesEuiccClient::Properties* euicc_properties =
HermesEuiccClient::Get()->GetProperties(euicc_path);
ShillServiceClient::TestInterface* service_test =
ShillServiceClient::Get()->GetTestInterface();
service_test->AddService(service_path,
"esim_guid" + properties->iccid().value(),
properties->name().value(), shill::kTypeCellular,
shill::kStateIdle, true);
service_test->SetServiceProperty(
service_path, shill::kEidProperty,
base::Value(euicc_properties->eid().value()));
service_test->SetServiceProperty(service_path, shill::kIccidProperty,
base::Value(properties->iccid().value()));
service_test->SetServiceProperty(
service_path, shill::kImsiProperty,
base::Value(properties->iccid().value() + "-IMSI"));
service_test->SetServiceProperty(
service_path, shill::kActivationStateProperty,
base::Value(shill::kActivationStateActivated));
service_test->SetServiceProperty(service_path, shill::kAutoConnectProperty,
base::Value(true));
service_test->SetServiceProperty(service_path, shill::kConnectableProperty,
base::Value(false));
service_test->SetServiceProperty(service_path, shill::kVisibleProperty,
base::Value(true));
CreateDefaultModbApn(service_path);
ShillProfileClient::TestInterface* profile_test =
ShillProfileClient::Get()->GetTestInterface();
profile_test->AddService(ShillProfileClient::GetSharedProfilePath(),
service_path);
}
void FakeHermesEuiccClient::CreateDefaultModbApn(
const std::string& service_path) {
ShillServiceClient::TestInterface* service_test =
ShillServiceClient::Get()->GetTestInterface();
service_test->SetServiceProperty(
service_path, shill::kCellularLastGoodApnProperty,
base::Value(service_test->GetFakeDefaultModbApnDict()));
service_test->SetServiceProperty(
service_path, shill::kCellularApnProperty,
base::Value(service_test->GetFakeDefaultModbApnDict()));
base::Value::List apn_list;
apn_list.Append(service_test->GetFakeDefaultModbApnDict());
ShillDeviceClient::TestInterface* device_test =
ShillDeviceClient::Get()->GetTestInterface();
DCHECK(device_test);
std::string device_path =
device_test->GetDevicePathForType(shill::kTypeCellular);
CHECK(!device_path.empty());
device_test->SetDeviceProperty(device_path, shill::kCellularApnListProperty,
base::Value(std::move(apn_list)),
/*notify_change=*/false);
}
void FakeHermesEuiccClient::CallNotifyPropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&FakeHermesEuiccClient::NotifyPropertyChanged,
base::Unretained(this), object_path, property_name));
}
void FakeHermesEuiccClient::NotifyPropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) {
if (property_name == hermes::euicc::kInstalledProfilesProperty ||
property_name == hermes::euicc::kPendingProfilesProperty ||
property_name == hermes::euicc::kProfilesProperty) {
UpdateShillDeviceSimSlotInfo();
}
DVLOG(1) << "Property changed path=" << object_path.value()
<< ", property=" << property_name;
for (auto& observer : observers()) {
observer.OnEuiccPropertyChanged(object_path, property_name);
}
}
void FakeHermesEuiccClient::QueueInstalledProfile(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& profile_path) {
auto iter = installed_profile_queue_map_.find(euicc_path);
if (iter != installed_profile_queue_map_.end()) {
iter->second->push(profile_path);
return;
}
std::unique_ptr<InstalledProfileQueue> installed_profile_queue =
std::make_unique<InstalledProfileQueue>();
installed_profile_queue->push(profile_path);
installed_profile_queue_map_[euicc_path] = std::move(installed_profile_queue);
}
} // namespace ash