Commit ce7367c8 authored by Azeem Arshad's avatar Azeem Arshad Committed by Commit Bot

[CrOS Cellular] Add ESimManager mojo interface

This CL adds ESimManager class and mojo interface.
ESimManager communicates with Hermes, the ChromeOS
eSIM LPA daemon, to provide eSIM management methods
over mojo. See http://go/cros-esim-design-doc for
details.

An in-process instance of ESimManager is owned in Ash
in the same way as network_config service.

Bug: 1093185
Change-Id: I444ae644d020c7df7eea68025d1cb479cdec7c11
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2315925
Commit-Queue: Azeem Arshad <azeemarshad@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798685}
parent 52c4880c
......@@ -117,6 +117,8 @@ component("cpp") {
"default_scale_factor_retriever.h",
"desks_helper.cc",
"desks_helper.h",
"esim_manager.cc",
"esim_manager.h",
"fps_counter.cc",
"fps_counter.h",
"frame_header.cc",
......@@ -306,6 +308,7 @@ component("cpp") {
"//chromeos/constants",
"//chromeos/dbus/power:power_manager_proto",
"//chromeos/services/assistant/public/cpp",
"//chromeos/services/cellular_setup:in_process_esim_manager",
"//chromeos/services/network_config:in_process_instance",
"//components/prefs",
"//components/sync:rest_of_sync",
......@@ -330,6 +333,7 @@ component("cpp") {
"//base",
"//chromeos/components/security_token_pin",
"//chromeos/services/assistant/public/mojom",
"//chromeos/services/cellular_setup/public/mojom",
"//chromeos/services/network_config/public/mojom",
"//components/arc/mojom:notifications",
"//components/session_manager:base",
......
// Copyright 2020 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 "ash/public/cpp/esim_manager.h"
#include "chromeos/services/cellular_setup/in_process_esim_manager.h"
namespace ash {
void GetESimManager(
mojo::PendingReceiver<chromeos::cellular_setup::mojom::ESimManager>
receiver) {
chromeos::cellular_setup::BindToInProcessESimManager(std::move(receiver));
}
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_PUBLIC_CPP_ESIM_MANAGER_H_
#define ASH_PUBLIC_CPP_ESIM_MANAGER_H_
#include "ash/public/cpp/ash_public_export.h"
#include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
namespace ash {
ASH_PUBLIC_EXPORT void GetESimManager(
mojo::PendingReceiver<chromeos::cellular_setup::mojom::ESimManager>
receiver);
} // namespace ash
#endif // ASH_PUBLIC_CPP_ESIM_MANAGER_H_
......@@ -26,6 +26,42 @@ static_library("cellular_setup") {
]
}
static_library("esim_manager") {
sources = [
"esim_manager.cc",
"esim_manager.h",
]
deps = [
"//base",
"//chromeos/dbus/hermes",
"//chromeos/services/cellular_setup/public/mojom",
"//components/device_event_log",
"//dbus",
]
}
component("in_process_esim_manager") {
sources = [
"in_process_esim_manager.cc",
"in_process_esim_manager.h",
]
defines = [ "IS_IN_PROCESS_ESIM_MANAGER_IMPL" ]
public_deps = [
"//chromeos/services/cellular_setup/public/mojom",
"//mojo/public/cpp/bindings",
]
deps = [
":esim_manager",
"//chromeos/services/cellular_setup/public/mojom",
"//dbus",
"//mojo/public/cpp/bindings",
]
}
static_library("test_support") {
testonly = true
......@@ -47,17 +83,21 @@ source_set("unit_tests") {
sources = [
"cellular_setup_impl_unittest.cc",
"cellular_setup_service_unittest.cc",
"esim_manager_unittest.cc",
"ota_activator_impl_unittest.cc",
]
deps = [
":cellular_setup",
":esim_manager",
":test_support",
"//base",
"//base/test:test_support",
"//chromeos/dbus/hermes",
"//chromeos/dbus/shill",
"//chromeos/network:test_support",
"//chromeos/services/cellular_setup/public/cpp:test_support",
"//dbus",
"//testing/gmock",
"//testing/gtest",
]
......
// Copyright 2020 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/services/cellular_setup/esim_manager.h"
#include "base/strings/utf_string_conversions.h"
#include "components/device_event_log/device_event_log.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
namespace chromeos {
namespace cellular_setup {
namespace {
mojom::ProfileState ProfileStateToMojo(int32_t state) {
switch (state) {
case hermes::profile::State::kActive:
return mojom::ProfileState::kActive;
case hermes::profile::State::kInactive:
return mojom::ProfileState::kInactive;
default:
return mojom::ProfileState::kPending;
}
}
mojom::ESimManager::ProfileInstallResult InstallResultFromStatus(
HermesResponseStatus status) {
switch (status) {
case HermesResponseStatus::kSuccess:
return ESimManager::ProfileInstallResult::kSuccess;
case HermesResponseStatus::kErrorNeedConfirmationCode:
return ESimManager::ProfileInstallResult::kErrorNeedsConfirmationCode;
case HermesResponseStatus::kErrorInvalidActivationCode:
return ESimManager::ProfileInstallResult::kErrorInvalidActivationCode;
default:
return ESimManager::ProfileInstallResult::kFailure;
}
}
mojom::ESimManager::ESimOperationResult OperationResultFromStatus(
HermesResponseStatus status) {
switch (status) {
case HermesResponseStatus::kSuccess:
return ESimManager::ESimOperationResult::kSuccess;
default:
return ESimManager::ESimOperationResult::kFailure;
}
}
} // namespace
ESimManager::EuiccInfo::EuiccInfo(const dbus::ObjectPath& path,
HermesEuiccClient::Properties* properties)
: euicc_(mojom::Euicc::New()), path_(path) {
CopyProperties(properties);
}
ESimManager::EuiccInfo::~EuiccInfo() = default;
bool ESimManager::EuiccInfo::ContainsIccid(const std::string& iccid) {
return profile_iccids_.find(iccid) != profile_iccids_.end();
}
void ESimManager::EuiccInfo::CopyProperties(
HermesEuiccClient::Properties* properties) {
euicc_->eid = properties->eid().value();
euicc_->is_active = properties->is_active().value();
}
ESimManager::ESimProfileInfo::ESimProfileInfo(
const dbus::ObjectPath& path,
const std::string& eid,
HermesProfileClient::Properties* properties)
: esim_profile_(mojom::ESimProfile::New()), path_(path) {
CopyProperties(properties);
esim_profile_->eid = eid;
}
ESimManager::ESimProfileInfo::~ESimProfileInfo() = default;
void ESimManager::ESimProfileInfo::CopyProperties(
HermesProfileClient::Properties* properties) {
esim_profile_->iccid = properties->iccid().value();
esim_profile_->name = base::UTF8ToUTF16(properties->name().value());
esim_profile_->nickname = base::UTF8ToUTF16(properties->nick_name().value());
esim_profile_->service_provider =
base::UTF8ToUTF16(properties->service_provider().value());
esim_profile_->state = ProfileStateToMojo(properties->state().value());
esim_profile_->activation_code = properties->activation_code().value();
}
ESimManager::ESimManager()
: hermes_manager_client_(HermesManagerClient::Get()),
hermes_euicc_client_(HermesEuiccClient::Get()),
hermes_profile_client_(HermesProfileClient::Get()) {
hermes_manager_client_->AddObserver(this);
hermes_euicc_client_->AddObserver(this);
hermes_profile_client_->AddObserver(this);
UpdateAvailableEuiccs();
}
ESimManager::~ESimManager() {
hermes_manager_client_->RemoveObserver(this);
hermes_euicc_client_->RemoveObserver(this);
hermes_profile_client_->RemoveObserver(this);
}
void ESimManager::BindReceiver(
mojo::PendingReceiver<mojom::ESimManager> receiver) {
NET_LOG(EVENT) << "ESimManager::BindReceiver()";
receivers_.Add(this, std::move(receiver));
}
void ESimManager::AddObserver(
mojo::PendingRemote<mojom::ESimManagerObserver> observer) {
observers_.Add(std::move(observer));
}
void ESimManager::GetAvailableEuiccs(GetAvailableEuiccsCallback callback) {
std::vector<mojom::EuiccPtr> euicc_list;
for (auto const& eid : available_euicc_eids_)
euicc_list.push_back(eid_euicc_info_map_[eid]->euicc()->Clone());
std::move(callback).Run(std::move(euicc_list));
}
void ESimManager::GetProfiles(const std::string& eid,
GetProfilesCallback callback) {
if (!eid_euicc_info_map_.count(eid)) {
std::move(callback).Run(base::nullopt);
return;
}
EuiccInfo* euicc_info = eid_euicc_info_map_[eid].get();
std::vector<mojom::ESimProfilePtr> profile_list;
for (auto const& iccid : euicc_info->profile_iccids()) {
profile_list.push_back(
iccid_esim_profile_info_map_[iccid]->esim_profile()->Clone());
}
std::move(callback).Run(std::move(profile_list));
}
void ESimManager::RequestPendingProfiles(
const std::string& eid,
RequestPendingProfilesCallback callback) {
if (!eid_euicc_info_map_.count(eid)) {
NET_LOG(ERROR) << "RequestPendingProfiles failed: Unknown eid.";
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
NET_LOG(EVENT) << "Requesting Pending profiles";
hermes_euicc_client_->RequestPendingEvents(
eid_euicc_info_map_[eid]->path(),
base::BindOnce(&ESimManager::OnRequestPendingEventsResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ESimManager::InstallProfileFromActivationCode(
const std::string& eid,
const std::string& activation_code,
const std::string& confirmation_code,
InstallProfileFromActivationCodeCallback callback) {
ESimProfileInfo* profile_info = nullptr;
ProfileInstallResult status = GetPendingProfileInfoFromActivationCode(
activation_code, eid, &profile_info);
if (profile_info &&
status != mojom::ESimManager::ProfileInstallResult::kSuccess) {
// Return early if profile was found but not in the correct state.
std::move(callback).Run(status, nullptr);
return;
}
if (profile_info) {
CallInstallPendingProfile(profile_info, confirmation_code,
std::move(callback));
return;
}
// Try installing directly with activation code.
hermes_euicc_client_->InstallProfileFromActivationCode(
eid_euicc_info_map_[eid]->path(), activation_code, confirmation_code,
base::BindOnce(&ESimManager::OnProfileInstallResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), eid));
}
void ESimManager::InstallProfile(const std::string& iccid,
const std::string& confirmation_code,
InstallProfileCallback callback) {
ESimProfileInfo* profile_info = GetPendingProfileInfoFromIccid(iccid);
if (!profile_info) {
std::move(callback).Run(ProfileInstallResult::kFailure, nullptr);
return;
}
CallInstallPendingProfile(profile_info, confirmation_code,
std::move(callback));
}
void ESimManager::UninstallProfile(const std::string& iccid,
UninstallProfileCallback callback) {
ESimProfileInfo* profile_info = GetInstalledProfileInfoFromIccid(iccid);
if (!profile_info) {
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
EuiccInfo* euicc_info =
eid_euicc_info_map_[profile_info->esim_profile()->eid].get();
hermes_euicc_client_->UninstallProfile(
euicc_info->path(), profile_info->path(),
base::BindOnce(&ESimManager::OnESimOperationResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ESimManager::EnableProfile(const std::string& iccid,
EnableProfileCallback callback) {
ESimProfileInfo* profile_info = GetInstalledProfileInfoFromIccid(iccid);
if (!profile_info) {
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
if (profile_info->esim_profile()->state == mojom::ProfileState::kActive) {
NET_LOG(ERROR) << "Profile enable failed: Profile already enabled";
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
hermes_profile_client_->EnableCarrierProfile(
profile_info->path(),
base::BindOnce(&ESimManager::OnESimOperationResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ESimManager::DisableProfile(const std::string& iccid,
DisableProfileCallback callback) {
ESimProfileInfo* profile_info = GetInstalledProfileInfoFromIccid(iccid);
if (!profile_info) {
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
if (profile_info->esim_profile()->state == mojom::ProfileState::kInactive) {
NET_LOG(ERROR) << "Profile enable failed: Profile already disabled";
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
hermes_profile_client_->DisableCarrierProfile(
profile_info->path(),
base::BindOnce(&ESimManager::OnESimOperationResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ESimManager::SetProfileNickname(const std::string& iccid,
const base::string16& nickname,
SetProfileNicknameCallback callback) {
ESimProfileInfo* profile_info = GetInstalledProfileInfoFromIccid(iccid);
if (!profile_info) {
std::move(callback).Run(ESimOperationResult::kFailure);
return;
}
HermesProfileClient::Properties* properties =
hermes_profile_client_->GetProperties(profile_info->path());
properties->nick_name().Set(
base::UTF16ToUTF8(nickname),
base::BindOnce(&ESimManager::OnProfilePropertySet,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ESimManager::OnAvailableEuiccListChanged() {
UpdateAvailableEuiccs();
for (auto& observer : observers_)
observer->OnAvailableEuiccListChanged();
}
void ESimManager::OnEuiccPropertyChanged(const dbus::ObjectPath& euicc_path,
const std::string& property_name) {
EuiccInfo* euicc_info = GetEuiccInfoFromPath(euicc_path);
// Skip notifying observers if the euicc object is not tracked.
if (!euicc_info)
return;
if (property_name == hermes::euicc::kPendingProfilesProperty ||
property_name == hermes::euicc::kInstalledProfilesProperty) {
UpdateProfileList(euicc_info);
for (auto& observer : observers_)
observer->OnProfileListChanged(euicc_info->euicc()->eid);
} else {
HermesEuiccClient::Properties* properties =
hermes_euicc_client_->GetProperties(euicc_path);
euicc_info->CopyProperties(properties);
for (auto& observer : observers_)
observer->OnEuiccChanged(euicc_info->euicc()->Clone());
}
}
void ESimManager::OnCarrierProfilePropertyChanged(
const dbus::ObjectPath& carrier_profile_path,
const std::string& property_name) {
ESimProfileInfo* profile_info = GetProfileInfoFromPath(carrier_profile_path);
// Skip notifying observers if the carrier profile is not tracked.
if (!profile_info)
return;
HermesProfileClient::Properties* properties =
hermes_profile_client_->GetProperties(carrier_profile_path);
profile_info->CopyProperties(properties);
for (auto& observer : observers_)
observer->OnProfileChanged(profile_info->esim_profile()->Clone());
}
void ESimManager::CallInstallPendingProfile(
ESimProfileInfo* profile_info,
const std::string& confirmation_code,
ProfileInstallResultCallback callback) {
mojom::ESimProfile* profile = profile_info->esim_profile();
if (profile->state == mojom::ProfileState::kInstalling ||
profile->state != mojom::ProfileState::kPending) {
NET_LOG(ERROR) << "Profile is already installed or in installing state.";
std::move(callback).Run(ProfileInstallResult::kFailure, nullptr);
return;
}
profile->state = mojom::ProfileState::kInstalling;
for (auto& observer : observers_)
observer->OnProfileChanged(profile->Clone());
hermes_euicc_client_->InstallPendingProfile(
eid_euicc_info_map_[profile->eid]->path(), profile_info->path(),
confirmation_code,
base::BindOnce(&ESimManager::OnPendingProfileInstallResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
profile_info));
}
void ESimManager::OnProfileInstallResult(ProfileInstallResultCallback callback,
const std::string& eid,
HermesResponseStatus status,
const dbus::ObjectPath* object_path) {
if (status != HermesResponseStatus::kSuccess) {
NET_LOG(ERROR) << "Error Installing profile status="
<< static_cast<int>(status);
std::move(callback).Run(InstallResultFromStatus(status), nullptr);
return;
}
HermesProfileClient::Properties* properties =
hermes_profile_client_->GetProperties(*object_path);
ESimProfileInfo* profile_info =
GetOrCreateESimProfileInfo(properties, *object_path, eid);
std::move(callback).Run(ProfileInstallResult::kSuccess,
profile_info->esim_profile()->Clone());
}
void ESimManager::OnPendingProfileInstallResult(
ProfileInstallResultCallback callback,
ESimProfileInfo* profile_info,
HermesResponseStatus status) {
if (status != HermesResponseStatus::kSuccess) {
NET_LOG(ERROR) << "Error Installing pending profile status="
<< static_cast<int>(status);
profile_info->esim_profile()->state = mojom::ProfileState::kPending;
for (auto& observer : observers_)
observer->OnProfileChanged(profile_info->esim_profile()->Clone());
std::move(callback).Run(InstallResultFromStatus(status), nullptr);
return;
}
std::move(callback).Run(ProfileInstallResult::kSuccess,
profile_info->esim_profile()->Clone());
}
void ESimManager::OnESimOperationResult(ESimOperationResultCallback callback,
HermesResponseStatus status) {
if (status != HermesResponseStatus::kSuccess) {
NET_LOG(ERROR) << "ESim operation error status="
<< static_cast<int>(status);
}
std::move(callback).Run(OperationResultFromStatus(status));
}
void ESimManager::OnProfilePropertySet(ESimOperationResultCallback callback,
bool success) {
if (!success) {
NET_LOG(ERROR) << "ESimProfile property set error.";
}
std::move(callback).Run(
success ? mojom::ESimManager::ESimOperationResult::kSuccess
: mojom::ESimManager::ESimOperationResult::kFailure);
}
void ESimManager::OnRequestPendingEventsResult(
RequestPendingProfilesCallback callback,
HermesResponseStatus status) {
if (status != HermesResponseStatus::kSuccess) {
NET_LOG(ERROR) << "Request Pending events failed status="
<< static_cast<int>(status);
}
std::move(callback).Run(
status == HermesResponseStatus::kSuccess
? mojom::ESimManager::ESimOperationResult::kSuccess
: mojom::ESimManager::ESimOperationResult::kFailure);
}
void ESimManager::UpdateAvailableEuiccs() {
NET_LOG(EVENT) << "Updating available Euiccs";
available_euicc_eids_.clear();
for (auto& euicc_path : hermes_manager_client_->GetAvailableEuiccs()) {
HermesEuiccClient::Properties* properties =
hermes_euicc_client_->GetProperties(euicc_path);
EuiccInfo* euicc_info = GetOrCreateEuiccInfo(properties, euicc_path);
available_euicc_eids_.insert(properties->eid().value());
UpdateProfileList(euicc_info);
}
RemoveUntrackedEuiccs();
}
void ESimManager::UpdateProfileList(EuiccInfo* euicc_info) {
HermesEuiccClient::Properties* euicc_properties =
hermes_euicc_client_->GetProperties(euicc_info->path());
std::set<std::string> profile_iccids;
for (auto& path : euicc_properties->installed_carrier_profiles().value()) {
HermesProfileClient::Properties* profile_properties =
hermes_profile_client_->GetProperties(path);
GetOrCreateESimProfileInfo(profile_properties, path,
euicc_info->euicc()->eid);
profile_iccids.insert(profile_properties->iccid().value());
}
for (auto& path : euicc_properties->pending_carrier_profiles().value()) {
HermesProfileClient::Properties* profile_properties =
hermes_profile_client_->GetProperties(path);
GetOrCreateESimProfileInfo(profile_properties, path,
euicc_info->euicc()->eid);
profile_iccids.insert(profile_properties->iccid().value());
}
euicc_info->set_profile_iccids(profile_iccids);
RemoveUntrackedProfiles(euicc_info);
}
void ESimManager::RemoveUntrackedEuiccs() {
for (auto euicc_it = eid_euicc_info_map_.begin();
euicc_it != eid_euicc_info_map_.end();) {
const std::string& eid = euicc_it->first;
if (available_euicc_eids_.find(eid) != available_euicc_eids_.end()) {
euicc_it++;
continue;
}
// Remove all profiles for this Euicc.
for (auto esim_profile_it = iccid_esim_profile_info_map_.begin();
esim_profile_it != iccid_esim_profile_info_map_.end();) {
if (esim_profile_it->second->esim_profile()->eid == eid)
esim_profile_it = iccid_esim_profile_info_map_.erase(esim_profile_it);
else
esim_profile_it++;
}
euicc_it = eid_euicc_info_map_.erase(euicc_it);
}
}
void ESimManager::RemoveUntrackedProfiles(EuiccInfo* euicc_info) {
for (auto it = iccid_esim_profile_info_map_.begin();
it != iccid_esim_profile_info_map_.end();) {
ESimProfileInfo* profile_info = it->second.get();
const std::string& iccid = it->first;
if (profile_info->esim_profile()->eid == euicc_info->euicc()->eid &&
!euicc_info->ContainsIccid(iccid)) {
it = iccid_esim_profile_info_map_.erase(it);
} else {
it++;
}
}
}
ESimManager::EuiccInfo* ESimManager::GetOrCreateEuiccInfo(
HermesEuiccClient::Properties* properties,
const dbus::ObjectPath& euicc_path) {
EuiccInfo* euicc_info = GetEuiccInfoFromPath(euicc_path);
if (euicc_info)
return euicc_info;
const std::string& eid = properties->eid().value();
eid_euicc_info_map_[eid] =
std::make_unique<EuiccInfo>(euicc_path, properties);
return eid_euicc_info_map_[eid].get();
}
ESimManager::ESimProfileInfo* ESimManager::GetOrCreateESimProfileInfo(
HermesProfileClient::Properties* properties,
const dbus::ObjectPath& carrier_profile_path,
const std::string& eid) {
ESimProfileInfo* profile_info = GetProfileInfoFromPath(carrier_profile_path);
if (profile_info)
return profile_info;
const std::string& iccid = properties->iccid().value();
iccid_esim_profile_info_map_[iccid] =
std::make_unique<ESimProfileInfo>(carrier_profile_path, eid, properties);
return iccid_esim_profile_info_map_[iccid].get();
}
ESimManager::EuiccInfo* ESimManager::GetEuiccInfoFromPath(
const dbus::ObjectPath& path) {
for (auto& pair : eid_euicc_info_map_) {
if (pair.second->path() == path) {
return eid_euicc_info_map_[pair.first].get();
}
}
return nullptr;
}
ESimManager::ESimProfileInfo* ESimManager::GetProfileInfoFromPath(
const dbus::ObjectPath& path) {
for (auto& pair : iccid_esim_profile_info_map_) {
if (pair.second->path() == path) {
return pair.second.get();
}
}
return nullptr;
}
mojom::ESimManager::ProfileInstallResult
ESimManager::GetPendingProfileInfoFromActivationCode(
const std::string& eid,
const std::string& activation_code,
ESimProfileInfo** profile_info) {
const auto iter = std::find_if(
iccid_esim_profile_info_map_.begin(), iccid_esim_profile_info_map_.end(),
[activation_code](const auto& pair) -> bool {
return pair.second->esim_profile()->activation_code == activation_code;
});
if (iter == iccid_esim_profile_info_map_.end()) {
NET_LOG(EVENT) << "Get pending profile with activation failed: No profile "
"with activation_code.";
return ProfileInstallResult::kFailure;
}
*profile_info = iter->second.get();
if (iter->second->esim_profile()->state != mojom::ProfileState::kPending ||
iter->second->esim_profile()->eid != eid) {
NET_LOG(ERROR) << "Get pending profile with activation code failed: Profile"
"is not in pending state or EID does not match.";
return ProfileInstallResult::kFailure;
}
return ProfileInstallResult::kSuccess;
}
ESimManager::ESimProfileInfo* ESimManager::GetPendingProfileInfoFromIccid(
const std::string& iccid) {
if (!iccid_esim_profile_info_map_.count(iccid)) {
NET_LOG(ERROR) << "Get pending profile with iccid failed: Unknown ICCID.";
return nullptr;
}
mojom::ESimProfile* profile =
iccid_esim_profile_info_map_[iccid]->esim_profile();
if (profile->state != mojom::ProfileState::kPending) {
NET_LOG(ERROR) << "Get pending profile with iccid failed: Profile is "
"already installed or installing.";
return nullptr;
}
return iccid_esim_profile_info_map_[iccid].get();
}
ESimManager::ESimProfileInfo* ESimManager::GetInstalledProfileInfoFromIccid(
const std::string& iccid) {
if (!iccid_esim_profile_info_map_.count(iccid)) {
NET_LOG(ERROR) << "Get installed profile with iccid failed: Unknown iccid.";
return nullptr;
}
mojom::ProfileState state =
iccid_esim_profile_info_map_[iccid]->esim_profile()->state;
if (state == mojom::ProfileState::kInstalling ||
state == mojom::ProfileState::kPending) {
NET_LOG(ERROR) << "Get installed profile with iccid failed. Invalid state="
<< state;
return nullptr;
}
return iccid_esim_profile_info_map_[iccid].get();
}
} // namespace cellular_setup
} // namespace chromeos
// Copyright 2020 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.
#ifndef CHROMEOS_SERVICES_CELLULAR_SETUP_ESIM_MANAGER_H_
#define CHROMEOS_SERVICES_CELLULAR_SETUP_ESIM_MANAGER_H_
#include "base/memory/weak_ptr.h"
#include "chromeos/dbus/hermes/hermes_euicc_client.h"
#include "chromeos/dbus/hermes/hermes_manager_client.h"
#include "chromeos/dbus/hermes/hermes_profile_client.h"
#include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
namespace chromeos {
namespace cellular_setup {
// Implementation of mojom::ESimManager. This class uses the Hermes
// DBus clients to communicate with the Hermes daemon and provide
// eSIM management methods. ESimManager mojo interface is provided
// in WebUI for cellular settings and eSIM setup.
class ESimManager : public mojom::ESimManager,
HermesManagerClient::Observer,
HermesEuiccClient::Observer,
HermesProfileClient::Observer {
public:
ESimManager();
ESimManager(const ESimManager&) = delete;
ESimManager& operator=(const ESimManager&) = delete;
~ESimManager() override;
void BindReceiver(mojo::PendingReceiver<mojom::ESimManager> receiver);
// mojom::ESimManager
void AddObserver(
mojo::PendingRemote<mojom::ESimManagerObserver> observer) override;
void GetAvailableEuiccs(GetAvailableEuiccsCallback callback) override;
void GetProfiles(const std::string& eid,
GetProfilesCallback callback) override;
void RequestPendingProfiles(const std::string& eid,
RequestPendingProfilesCallback callback) override;
void InstallProfileFromActivationCode(
const std::string& eid,
const std::string& activation_code,
const std::string& confirmation_code,
InstallProfileFromActivationCodeCallback callback) override;
void InstallProfile(const std::string& iccid,
const std::string& confirmation_code,
InstallProfileCallback callback) override;
void UninstallProfile(const std::string& iccid,
UninstallProfileCallback callback) override;
void EnableProfile(const std::string& iccid,
EnableProfileCallback callback) override;
void DisableProfile(const std::string& iccid,
DisableProfileCallback callback) override;
void SetProfileNickname(const std::string& iccid,
const base::string16& nickname,
SetProfileNicknameCallback callback) override;
// HermesManagerClient::Observer:
void OnAvailableEuiccListChanged() override;
// HermesEuiccClient::Observer:
void OnEuiccPropertyChanged(const dbus::ObjectPath& euicc_path,
const std::string& property_name) override;
// HermesProfileClient::Observer:
void OnCarrierProfilePropertyChanged(
const dbus::ObjectPath& carrier_profile_path,
const std::string& property_name) override;
private:
// EuiccInfo object is used to track state of Hermes Euicc objects.
// It holds mojom::Euicc object along with other related values.
class EuiccInfo {
public:
EuiccInfo(const dbus::ObjectPath& path,
HermesEuiccClient::Properties* properties);
~EuiccInfo();
// Returns a boolean indicating whether |iccid| exists
// in installed or pending profiles for this Euicc.
bool ContainsIccid(const std::string& iccid);
void CopyProperties(HermesEuiccClient::Properties* properties);
mojom::Euicc* euicc() { return euicc_.get(); }
const dbus::ObjectPath& path() { return path_; }
void set_profile_iccids(const std::set<std::string>& profile_iccids) {
profile_iccids_ = std::move(profile_iccids);
}
const std::set<std::string>& profile_iccids() { return profile_iccids_; }
private:
mojom::EuiccPtr euicc_;
dbus::ObjectPath path_;
std::set<std::string> profile_iccids_;
};
// ESimProfileInfo is used to track state of Hermes Profile objects.
// It holds mojom::ESimProfile object along with other related values.
class ESimProfileInfo {
public:
ESimProfileInfo(const dbus::ObjectPath& path,
const std::string& eid,
HermesProfileClient::Properties* properties);
~ESimProfileInfo();
void CopyProperties(HermesProfileClient::Properties* properties);
mojom::ESimProfile* esim_profile() { return esim_profile_.get(); }
const dbus::ObjectPath& path() { return path_; }
private:
mojom::ESimProfilePtr esim_profile_;
dbus::ObjectPath path_;
};
// Type of callback for profile installation methods.
using ProfileInstallResultCallback =
base::OnceCallback<void(mojom::ESimManager::ProfileInstallResult,
mojom::ESimProfilePtr)>;
// Type of callback for other esim manager methods.
using ESimOperationResultCallback =
base::OnceCallback<void(mojom::ESimManager::ESimOperationResult)>;
void CallInstallPendingProfile(ESimProfileInfo* profile_info,
const std::string& confirmation_code,
ProfileInstallResultCallback callback);
void OnRequestPendingEventsResult(RequestPendingProfilesCallback callback,
HermesResponseStatus status);
void OnPendingProfileInstallResult(ProfileInstallResultCallback callback,
ESimProfileInfo* profile_info,
HermesResponseStatus status);
void OnProfileInstallResult(ProfileInstallResultCallback callback,
const std::string& eid,
HermesResponseStatus status,
const dbus::ObjectPath* object_path);
void OnESimOperationResult(ESimOperationResultCallback callback,
HermesResponseStatus status);
void OnProfilePropertySet(ESimOperationResultCallback callback, bool success);
void UpdateAvailableEuiccs();
void UpdateProfileList(EuiccInfo* euicc_info);
void RemoveUntrackedEuiccs();
void RemoveUntrackedProfiles(EuiccInfo* euicc_info);
EuiccInfo* GetOrCreateEuiccInfo(HermesEuiccClient::Properties* properties,
const dbus::ObjectPath& euicc_path);
ESimProfileInfo* GetOrCreateESimProfileInfo(
HermesProfileClient::Properties* properties,
const dbus::ObjectPath& carrier_profile_path,
const std::string& eid);
EuiccInfo* GetEuiccInfoFromPath(const dbus::ObjectPath& path);
ESimProfileInfo* GetProfileInfoFromPath(const dbus::ObjectPath& path);
ProfileInstallResult GetPendingProfileInfoFromActivationCode(
const std::string& eid,
const std::string& activation_code,
ESimProfileInfo** profile_info);
ESimProfileInfo* GetPendingProfileInfoFromIccid(const std::string& iccid);
ESimProfileInfo* GetInstalledProfileInfoFromIccid(const std::string& iccid);
HermesManagerClient* hermes_manager_client_;
HermesEuiccClient* hermes_euicc_client_;
HermesProfileClient* hermes_profile_client_;
std::set<std::string> available_euicc_eids_;
std::map<std::string, std::unique_ptr<EuiccInfo>> eid_euicc_info_map_;
std::map<std::string, std::unique_ptr<ESimProfileInfo>>
iccid_esim_profile_info_map_;
mojo::RemoteSet<mojom::ESimManagerObserver> observers_;
mojo::ReceiverSet<mojom::ESimManager> receivers_;
base::WeakPtrFactory<ESimManager> weak_ptr_factory_{this};
};
} // namespace cellular_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_CELLULAR_SETUP_ESIM_MANAGER_H_
// Copyright 2020 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/services/cellular_setup/esim_manager.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "chromeos/dbus/hermes/hermes_clients.h"
#include "chromeos/dbus/hermes/hermes_manager_client.h"
#include "chromeos/dbus/shill/shill_clients.h"
#include "chromeos/dbus/shill/shill_manager_client.h"
#include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
namespace chromeos {
namespace cellular_setup {
namespace {
const char* kTestEuiccPath = "/org/chromium/Hermes/Euicc/0";
const char* kTestEid = "12345678901234567890123456789012";
void CopyESimProfileList(
std::vector<mojom::ESimProfilePtr>* dest,
base::OnceClosure quit_closure,
base::Optional<std::vector<mojom::ESimProfilePtr>> esim_profiles) {
if (esim_profiles) {
for (auto& euicc : *esim_profiles)
dest->push_back(std::move(euicc));
}
std::move(quit_closure).Run();
}
void CopyInstallResult(mojom::ESimManager::ProfileInstallResult* result_dest,
mojom::ESimProfilePtr* esim_profile_dest,
base::OnceClosure quit_closure,
mojom::ESimManager::ProfileInstallResult result,
mojom::ESimProfilePtr esim_profile) {
*result_dest = result;
*esim_profile_dest = std::move(esim_profile);
std::move(quit_closure).Run();
}
void CopyOperationResult(mojom::ESimManager::ESimOperationResult* result_dest,
base::OnceClosure quit_closure,
mojom::ESimManager::ESimOperationResult result) {
*result_dest = result;
std::move(quit_closure).Run();
}
} // namespace
// Fake observer for testing ESimManager.
class ESimManagerTestObserver : public mojom::ESimManagerObserver {
public:
ESimManagerTestObserver() = default;
ESimManagerTestObserver(const ESimManagerTestObserver&) = delete;
ESimManagerTestObserver& operator=(const ESimManagerTestObserver&) = delete;
~ESimManagerTestObserver() override = default;
// mojom::ESimManagerObserver:
void OnAvailableEuiccListChanged() override {
available_euicc_list_change_count_++;
}
void OnProfileListChanged(const std::string& eid) override {
profile_list_change_calls_.push_back(eid);
}
void OnEuiccChanged(mojom::EuiccPtr euicc) override {
euicc_change_calls_.push_back(std::move(euicc));
}
void OnProfileChanged(mojom::ESimProfilePtr esim_profile) override {
profile_change_calls_.push_back(std::move(esim_profile));
}
mojo::PendingRemote<mojom::ESimManagerObserver> GenerateRemote() {
return receiver_.BindNewPipeAndPassRemote();
}
void Reset() {
available_euicc_list_change_count_ = 0;
profile_list_change_calls_.clear();
euicc_change_calls_.clear();
profile_change_calls_.clear();
}
int available_euicc_list_change_count() {
return available_euicc_list_change_count_;
}
const std::vector<std::string>& profile_list_change_calls() {
return profile_list_change_calls_;
}
const std::vector<mojom::EuiccPtr>& euicc_change_calls() {
return euicc_change_calls_;
}
const std::vector<mojom::ESimProfilePtr>& profile_change_calls() {
return profile_change_calls_;
}
private:
int available_euicc_list_change_count_ = 0;
std::vector<std::string> profile_list_change_calls_;
std::vector<mojom::EuiccPtr> euicc_change_calls_;
std::vector<mojom::ESimProfilePtr> profile_change_calls_;
mojo::Receiver<mojom::ESimManagerObserver> receiver_{this};
};
class ESimManagerTest : public testing::Test {
public:
using InstallResultPair = std::pair<mojom::ESimManager::ProfileInstallResult,
mojom::ESimProfilePtr>;
ESimManagerTest() {
if (!ShillManagerClient::Get())
shill_clients::InitializeFakes();
if (!HermesManagerClient::Get())
hermes_clients::InitializeFakes();
}
ESimManagerTest(const ESimManagerTest&) = delete;
ESimManagerTest& operator=(const ESimManagerTest&) = delete;
~ESimManagerTest() override = default;
void SetUp() override {
HermesManagerClient::Get()->GetTestInterface()->ClearEuiccs();
HermesEuiccClient::Get()->GetTestInterface()->SetInteractiveDelay(
base::TimeDelta::FromSeconds(0));
esim_manager_ = std::make_unique<ESimManager>();
observer_ = std::make_unique<ESimManagerTestObserver>();
esim_manager_->AddObserver(observer_->GenerateRemote());
}
void TearDown() override {
esim_manager_.reset();
observer_.reset();
HermesEuiccClient::Get()->GetTestInterface()->ResetPendingEventsRequested();
}
void SetupEuicc() {
HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
dbus::ObjectPath(kTestEuiccPath), kTestEid, true);
base::RunLoop().RunUntilIdle();
}
std::vector<mojom::EuiccPtr> GetAvailableEuiccs() {
std::vector<mojom::EuiccPtr> result;
base::RunLoop run_loop;
esim_manager_->GetAvailableEuiccs(base::BindOnce(
[](std::vector<mojom::EuiccPtr>* result, base::OnceClosure quit_closure,
std::vector<mojom::EuiccPtr> available_euiccs) {
for (auto& euicc : available_euiccs)
result->push_back(std::move(euicc));
std::move(quit_closure).Run();
},
&result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
std::vector<mojom::ESimProfilePtr> GetProfiles(const std::string& eid) {
std::vector<mojom::ESimProfilePtr> result;
base::RunLoop run_loop;
esim_manager_->GetProfiles(
eid,
base::BindOnce(&CopyESimProfileList, &result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
mojom::ESimManager::ESimOperationResult RequestPendingProfiles(
const std::string& eid) {
mojom::ESimManager::ESimOperationResult result;
base::RunLoop run_loop;
esim_manager_->RequestPendingProfiles(
eid, base::BindOnce(
[](mojom::ESimManager::ESimOperationResult* result,
base::OnceClosure quit_closure,
mojom::ESimManager::ESimOperationResult status) {
*result = status;
std::move(quit_closure).Run();
},
&result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
InstallResultPair InstallProfileFromActivationCode(
const std::string& eid,
const std::string& activation_code,
const std::string& confirmation_code) {
base::RunLoop run_loop;
mojom::ESimManager::ProfileInstallResult result;
mojom::ESimProfilePtr esim_profile;
esim_manager_->InstallProfileFromActivationCode(
eid, activation_code, confirmation_code,
base::BindOnce(&CopyInstallResult, &result, &esim_profile,
run_loop.QuitClosure()));
run_loop.Run();
return std::make_pair(result, std::move(esim_profile));
}
InstallResultPair InstallProfile(const std::string& iccid,
const std::string& confirmation_code) {
base::RunLoop run_loop;
mojom::ESimManager::ProfileInstallResult result;
mojom::ESimProfilePtr esim_profile;
esim_manager_->InstallProfile(
iccid, confirmation_code,
base::BindOnce(&CopyInstallResult, &result, &esim_profile,
run_loop.QuitClosure()));
run_loop.Run();
return std::make_pair(result, std::move(esim_profile));
}
mojom::ESimManager::ESimOperationResult UninstallProfile(
const std::string& iccid) {
base::RunLoop run_loop;
mojom::ESimManager::ESimOperationResult result;
esim_manager_->UninstallProfile(
iccid,
base::BindOnce(&CopyOperationResult, &result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
mojom::ESimManager::ESimOperationResult EnableProfile(
const std::string& iccid) {
base::RunLoop run_loop;
mojom::ESimManager::ESimOperationResult result;
esim_manager_->EnableProfile(
iccid,
base::BindOnce(&CopyOperationResult, &result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
mojom::ESimManager::ESimOperationResult DisableProfile(
const std::string& iccid) {
base::RunLoop run_loop;
mojom::ESimManager::ESimOperationResult result;
esim_manager_->DisableProfile(
iccid,
base::BindOnce(&CopyOperationResult, &result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
mojom::ESimManager::ESimOperationResult SetProfileNickname(
const std::string& iccid,
const base::string16& nickname) {
base::RunLoop run_loop;
mojom::ESimManager::ESimOperationResult result;
esim_manager_->SetProfileNickname(
iccid, nickname,
base::BindOnce(&CopyOperationResult, &result, run_loop.QuitClosure()));
run_loop.Run();
return result;
}
ESimManager* esim_manager() { return esim_manager_.get(); }
ESimManagerTestObserver* observer() { return observer_.get(); }
private:
base::test::SingleThreadTaskEnvironment task_environment_;
std::unique_ptr<ESimManager> esim_manager_;
std::unique_ptr<ESimManagerTestObserver> observer_;
};
TEST_F(ESimManagerTest, GetAvailableEuiccs) {
ASSERT_EQ(0u, GetAvailableEuiccs().size());
// Verify that Euicc List change is notified to observer when a
// a new Euicc is setup.
SetupEuicc();
EXPECT_EQ(1, observer()->available_euicc_list_change_count());
// Verify that GetAvailableEuiccs call returns list of euiccs.
std::vector<mojom::EuiccPtr> available_euiccs = GetAvailableEuiccs();
ASSERT_EQ(1u, available_euiccs.size());
EXPECT_EQ(kTestEid, available_euiccs.front()->eid);
}
TEST_F(ESimManagerTest, GetProfiles) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath active_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kActive, "");
dbus::ObjectPath pending_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
HermesProfileClient::Properties* active_profile_properties =
HermesProfileClient::Get()->GetProperties(active_profile_path);
HermesProfileClient::Properties* pending_profile_properties =
HermesProfileClient::Get()->GetProperties(pending_profile_path);
// Verify the profile list change is notified to observer.
ASSERT_EQ(2u, observer()->profile_list_change_calls().size());
EXPECT_EQ(kTestEid, observer()->profile_list_change_calls().at(0));
EXPECT_EQ(kTestEid, observer()->profile_list_change_calls().at(1));
// Verify that the added profile is returned in installed list.
std::vector<mojom::ESimProfilePtr> profile_list = GetProfiles(kTestEid);
ASSERT_EQ(2u, profile_list.size());
EXPECT_EQ(active_profile_properties->iccid().value(),
profile_list.at(0)->iccid);
EXPECT_EQ(pending_profile_properties->iccid().value(),
profile_list.at(1)->iccid);
}
TEST_F(ESimManagerTest, RequestPendingProfiles) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
// Verify that pending profile request errors are return properly.
euicc_test->QueueHermesErrorStatus(HermesResponseStatus::kErrorNoResponse);
mojom::ESimManager::ESimOperationResult result =
RequestPendingProfiles(kTestEid);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_list_change_calls().size());
// Verify that successful request notifies observers and returns correct
// status code.
result = RequestPendingProfiles(kTestEid);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kSuccess, result);
EXPECT_EQ(kTestEid, observer()->profile_list_change_calls().front());
}
TEST_F(ESimManagerTest, InstallProfileFromActivationCode) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
// Verify that install errors return error code properly.
euicc_test->QueueHermesErrorStatus(
HermesResponseStatus::kErrorInvalidActivationCode);
InstallResultPair result_pair =
InstallProfileFromActivationCode(kTestEid, "", "");
EXPECT_EQ(
mojom::ESimManager::ProfileInstallResult::kErrorInvalidActivationCode,
result_pair.first);
EXPECT_EQ(nullptr, result_pair.second.get());
// Verify that installing a profile returns proper status code
// and profile object.
dbus::ObjectPath profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
HermesProfileClient::Properties* properties =
HermesProfileClient::Get()->GetProperties(profile_path);
result_pair = InstallProfileFromActivationCode(
kTestEid, properties->activation_code().value(), "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ProfileInstallResult::kSuccess,
result_pair.first);
ASSERT_NE(nullptr, result_pair.second.get());
EXPECT_EQ(properties->iccid().value(), result_pair.second->iccid);
EXPECT_EQ(3u, observer()->profile_list_change_calls().size());
}
TEST_F(ESimManagerTest, InstallProfile) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
HermesProfileClient::Properties* properties =
HermesProfileClient::Get()->GetProperties(profile_path);
// Verify that install errors return error code properly.
euicc_test->QueueHermesErrorStatus(
HermesResponseStatus::kErrorNeedConfirmationCode);
InstallResultPair result_pair =
InstallProfile(properties->iccid().value(), "");
EXPECT_EQ(
mojom::ESimManager::ProfileInstallResult::kErrorNeedsConfirmationCode,
result_pair.first);
EXPECT_EQ(nullptr, result_pair.second.get());
// Verify that installing pending profile returns proper results.
result_pair = InstallProfile(properties->iccid().value(), "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ProfileInstallResult::kSuccess,
result_pair.first);
ASSERT_NE(nullptr, result_pair.second.get());
ASSERT_EQ(properties->iccid().value(), result_pair.second->iccid);
EXPECT_EQ(3u, observer()->profile_list_change_calls().size());
}
TEST_F(ESimManagerTest, UninstallProfile) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath active_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kActive, "");
dbus::ObjectPath pending_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, observer()->profile_list_change_calls().size());
observer()->Reset();
HermesProfileClient::Properties* pending_profile_properties =
HermesProfileClient::Get()->GetProperties(pending_profile_path);
HermesProfileClient::Properties* active_profile_properties =
HermesProfileClient::Get()->GetProperties(active_profile_path);
// Verify that uninstall error codes are returned properly.
euicc_test->QueueHermesErrorStatus(
HermesResponseStatus::kErrorInvalidResponse);
mojom::ESimManager::ESimOperationResult result =
UninstallProfile(active_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_list_change_calls().size());
// Verify that pending profiles cannot be uninstalled
observer()->Reset();
result = UninstallProfile(pending_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_list_change_calls().size());
// Verify that uninstall removes the profile and notifies observers properly.
observer()->Reset();
result = UninstallProfile(active_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kSuccess, result);
ASSERT_EQ(1u, observer()->profile_list_change_calls().size());
EXPECT_EQ(kTestEid, observer()->profile_list_change_calls().front());
}
TEST_F(ESimManagerTest, EnableProfile) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath inactive_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kInactive, "");
dbus::ObjectPath pending_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, observer()->profile_list_change_calls().size());
observer()->Reset();
HermesProfileClient::Properties* pending_profile_properties =
HermesProfileClient::Get()->GetProperties(pending_profile_path);
HermesProfileClient::Properties* inactive_profile_properties =
HermesProfileClient::Get()->GetProperties(inactive_profile_path);
// Verify that pending profiles cannot be enabled.
mojom::ESimManager::ESimOperationResult result =
EnableProfile(pending_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_change_calls().size());
// Verify that enabling profile returns result properly.
result = EnableProfile(inactive_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kSuccess, result);
EXPECT_EQ(inactive_profile_properties->iccid().value(),
observer()->profile_change_calls().front()->iccid);
EXPECT_EQ(mojom::ProfileState::kActive,
observer()->profile_change_calls().front()->state);
}
TEST_F(ESimManagerTest, DisableProfile) {
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath active_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kActive, "");
dbus::ObjectPath pending_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, observer()->profile_list_change_calls().size());
observer()->Reset();
HermesProfileClient::Properties* pending_profile_properties =
HermesProfileClient::Get()->GetProperties(pending_profile_path);
HermesProfileClient::Properties* active_profile_properties =
HermesProfileClient::Get()->GetProperties(active_profile_path);
// Verify that pending profiles cannot be disabled.
mojom::ESimManager::ESimOperationResult result =
DisableProfile(pending_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_change_calls().size());
// Verify that disabling profile returns result properly.
result = DisableProfile(active_profile_properties->iccid().value());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kSuccess, result);
EXPECT_EQ(active_profile_properties->iccid().value(),
observer()->profile_change_calls().front()->iccid);
EXPECT_EQ(mojom::ProfileState::kInactive,
observer()->profile_change_calls().front()->state);
}
TEST_F(ESimManagerTest, SetProfileNickName) {
const base::string16 test_nickname = base::UTF8ToUTF16("Test nickname");
SetupEuicc();
HermesEuiccClient::TestInterface* euicc_test =
HermesEuiccClient::Get()->GetTestInterface();
dbus::ObjectPath active_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kActive, "");
dbus::ObjectPath pending_profile_path = euicc_test->AddFakeCarrierProfile(
dbus::ObjectPath(kTestEuiccPath), hermes::profile::State::kPending, "");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, observer()->profile_list_change_calls().size());
observer()->Reset();
HermesProfileClient::Properties* pending_profile_properties =
HermesProfileClient::Get()->GetProperties(pending_profile_path);
HermesProfileClient::Properties* active_profile_properties =
HermesProfileClient::Get()->GetProperties(active_profile_path);
// Verify that pending profiles cannot be modified.
mojom::ESimManager::ESimOperationResult result = SetProfileNickname(
pending_profile_properties->iccid().value(), test_nickname);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kFailure, result);
EXPECT_EQ(0u, observer()->profile_change_calls().size());
// Verify that nickname can be set on active profiles.
result = SetProfileNickname(active_profile_properties->iccid().value(),
test_nickname);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mojom::ESimManager::ESimOperationResult::kSuccess, result);
EXPECT_EQ(active_profile_properties->iccid().value(),
observer()->profile_change_calls().front()->iccid);
EXPECT_EQ(test_nickname,
observer()->profile_change_calls().front()->nickname);
}
} // namespace cellular_setup
} // namespace chromeos
// Copyright 2020 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/services/cellular_setup/in_process_esim_manager.h"
#include "base/no_destructor.h"
#include "chromeos/services/cellular_setup/esim_manager.h"
namespace chromeos {
namespace cellular_setup {
void BindToInProcessESimManager(
mojo::PendingReceiver<mojom::ESimManager> receiver) {
static base::NoDestructor<ESimManager> instance;
instance->BindReceiver(std::move(receiver));
}
} // namespace cellular_setup
} // namespace chromeos
// Copyright 2020 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.
#ifndef CHROMEOS_SERVICES_CELLULAR_SETUP_IN_PROCESS_ESIM_MANAGER_H_
#define CHROMEOS_SERVICES_CELLULAR_SETUP_IN_PROCESS_ESIM_MANAGER_H_
#include "base/component_export.h"
#include "chromeos/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
namespace chromeos {
namespace cellular_setup {
COMPONENT_EXPORT(IN_PROCESS_ESIM_MANAGER)
void BindToInProcessESimManager(
mojo::PendingReceiver<mojom::ESimManager> receiver);
} // namespace cellular_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_CELLULAR_SETUP_IN_PROCESS_ESIM_MANAGER_H_
......@@ -5,7 +5,13 @@
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "cellular_setup.mojom" ]
sources = [
"cellular_setup.mojom",
"esim_manager.mojom",
]
public_deps = [ "//url/mojom:url_mojom_gurl" ]
public_deps = [
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
]
}
// Copyright 2020 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.
module chromeos.cellular_setup.mojom;
import "mojo/public/mojom/base/string16.mojom";
// Represents the current state of an ESimProfile
enum ProfileState {
kPending, // Profile is not installed on the device.
kInstalling, // Profile is being installed.
kInactive, // Profile is installed but inactive.
kActive, // Profile is installed and active.
};
// Represents an EUICC (Embedded Universal Integrated Circuit Card) hardware
// available on the device. Each Euicc may have multiple ESimProfiles
// installed on it.
struct Euicc {
// A 32 digit unique id for the Euicc.
string eid;
// Boolean indicating whether the Euicc is active or not.
bool is_active;
};
// Represents an eSIM profile.
struct ESimProfile {
// EID of the Euicc in which this profile is installed or available for
// installation.
string eid;
// A 19-20 character long numeric id that uniquely identifies this profile.
string iccid;
// Service provider’s name for this profile. e.g. “Verizon Plan 1”
mojo_base.mojom.String16 name;
// A user configurable nickname for this profile. e.g. “My Personal SIM”
mojo_base.mojom.String16 nickname;
// Name of the service provider. e.g. “Verizon Wireless”
mojo_base.mojom.String16 service_provider;
// State of the profile
ProfileState state;
// An alphanumeric code that can be used to install this profile.
string activation_code;
};
// ESimManagerObserver is implemented by any module that
// needs to observe changes to the eSIM module.
interface ESimManagerObserver {
// Called when a new Euicc is added or removed from the system.
OnAvailableEuiccListChanged();
// Called when a new eSIM profile is added or removed to the
// Euicc with the given |eid|.
OnProfileListChanged(string eid);
// Called when an Euicc object's active state changes.
OnEuiccChanged(Euicc euicc);
// Called when an eSIM profile’s state or nickname changes.
OnProfileChanged(ESimProfile profile);
};
// ESimManager interface provides eSIM profile management methods. An instance
// of this interface is owned in the browser process in Ash. It will be
// accessed from WebUI or System UI code in the browser process.
interface ESimManager {
// Result codes for profile installation.
enum ProfileInstallResult {
kSuccess, // Installation succeeded.
kFailure, // Installation failed.
kErrorNeedsConfirmationCode, // Installation requires a valid
// confirmation code.
kErrorInvalidActivationCode, // Given activation code is invalid.
};
// Result code for ESimManager methods.
enum ESimOperationResult {
kSuccess, // Operation succeeded.
kFailure // Operation failed.
};
// Adds an observer for eSIM changes.
AddObserver(pending_remote<ESimManagerObserver> observer);
// Returns a list of Euiccs available on the device.
GetAvailableEuiccs() => (array<Euicc> euiccs);
// Returns a list of all profiles installed or pending on the Euicc
// with given |eid|. An empty list could be returned if there are
// no installed or pending profiles on the Euicc.
GetProfiles(string eid) => (array<ESimProfile>? profiles);
// Starts a request for pending profiles for the Euicc with given |eid|
// from SMDS. Returns a status indicating result of the operation. This
// method updates the pending profiles list before succeeding. This method
// is used to allow the user to explicit trigger a request to check for
// new profiles that may have been pushed to SMDS.
RequestPendingProfiles(string eid) => (ESimOperationResult result);
// Installs a profile with given |activation_code| on the Euicc with
// given |eid| and returns the ESimProfile that was just installed. A
// non-success result code is returned in case of errors. |activation_code|
// may be obtained from ESimProfiles retrieved with a previous call to
// GetPendingProfiles or directly from the user through a QR code. If
// |confirmation_code| is required but not supplied or if it’s invalid
// then a kErrorNeedsConfirmationCode result is returned.
InstallProfileFromActivationCode(string eid, string activation_code,
string confirmation_code) =>
(ProfileInstallResult result, ESimProfile? profile);
// Installs an eSIM profile given it’s |iccid| and returns the
// ESimProfile that was just installed. A non success result code
// is returned in case of errors.
InstallProfile(string iccid, string confirmation_code) =>
(ProfileInstallResult result, ESimProfile? profile);
// Uninstalls an eSIM profile given it’s |iccid|. Returns the result
// code for the operation.
UninstallProfile(string iccid) => (ESimOperationResult result);
// Enables an eSIM profile with the given |iccid|. Returns the result
// code for the operation.
EnableProfile(string iccid) => (ESimOperationResult result);
// Disables an eSIM profile with the given |iccid|. Returns the result
// code for the operation.
DisableProfile(string iccid) => (ESimOperationResult result);
// Sets a nickname for an eSIM profile with given |iccid|. Returns the
// result code for the operation.
SetProfileNickname(string iccid, mojo_base.mojom.String16 nickname) =>
(ESimOperationResult result);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment