1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_POLICY_HANDLER_H_
#define CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_POLICY_HANDLER_H_
#include <optional>
#include "base/component_export.h"
#include "base/containers/queue.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "chromeos/ash/components/network/cellular_esim_profile_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/ash/components/network/policy_util.h"
#include "net/base/backoff_entry.h"
namespace dbus {
class ObjectPath;
} // namespace dbus
namespace ash {
class CellularESimInstaller;
class CellularInhibitor;
class NetworkProfileHandler;
class NetworkStateHandler;
class ManagedCellularPrefHandler;
class ManagedNetworkConfigurationHandler;
enum class HermesResponseStatus;
// This class encapsulates the logic for installing eSIM profiles configured by
// policy. Installation requests are added to a queue, and each request will be
// retried a fixed number of times with a retry delay between each attempt.
class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularPolicyHandler
: public HermesManagerClient::Observer,
public CellularESimProfileHandler::Observer,
public NetworkStateHandlerObserver {
public:
CellularPolicyHandler();
CellularPolicyHandler(const CellularPolicyHandler&) = delete;
CellularPolicyHandler& operator=(const CellularPolicyHandler&) = delete;
~CellularPolicyHandler() override;
void Init(CellularESimProfileHandler* cellular_esim_profile_handler,
CellularESimInstaller* cellular_esim_installer,
CellularInhibitor* cellular_inhibitor,
NetworkProfileHandler* network_profile_handler,
NetworkStateHandler* network_state_handler,
ManagedCellularPrefHandler* managed_cellular_pref_handler,
ManagedNetworkConfigurationHandler*
managed_network_configuration_handler);
// Installs the policy eSIM profile defined in |onc_config|. The Shill service
// configuration will be updated to match the GUID provided by |onc_config|
// and to include the ICCID of the installed profile. Installations are
// performed using a queue, and each installation will be retried a fix number
// of times.
void InstallESim(const base::Value::Dict& onc_config);
private:
// This enum allows us to treat a retry differently depending on what the
// reason for retrying is.
enum class InstallRetryReason {
kMissingNonCellularConnectivity = 0,
kInternalError = 1,
kUserError = 2,
kOther = 3,
};
// HermesUserErrorCodes indicate errors made by the user. These can be due
// to bad input or a valid input that has already been successfully processed.
// In such errors, we do not attempt to retry.
const std::array<HermesResponseStatus, 4> kHermesUserErrorCodes = {
HermesResponseStatus::kErrorAlreadyDisabled,
HermesResponseStatus::kErrorAlreadyEnabled,
HermesResponseStatus::kErrorInvalidActivationCode,
HermesResponseStatus::kErrorInvalidIccid};
// HermesInternalErrorCodes indicate system failure during the installation
// process. These error can happen due to code bugs or reasons unrelated to
// user input. In these cases, we retry using an exponental backoff policy to
// attempt the installation again.
const std::array<HermesResponseStatus, 7> kHermesInternalErrorCodes = {
HermesResponseStatus::kErrorUnknown,
HermesResponseStatus::kErrorInternalLpaFailure,
HermesResponseStatus::kErrorWrongState,
HermesResponseStatus::kErrorSendApduFailure,
HermesResponseStatus::kErrorUnexpectedModemManagerState,
HermesResponseStatus::kErrorModemMessageProcessing,
HermesResponseStatus::kErrorPendingProfile};
friend class CellularPolicyHandlerTest;
// Represents policy eSIM install request parameters. Requests are queued and
// processed one at a time. |activation_code| represents the SM-DP+ activation
// code that will be used to install the eSIM profile, and |onc_config| is the
// ONC configuration of the cellular policy.
struct InstallPolicyESimRequest {
InstallPolicyESimRequest(policy_util::SmdxActivationCode activation_code,
const base::Value::Dict& onc_config);
InstallPolicyESimRequest(const InstallPolicyESimRequest&) = delete;
InstallPolicyESimRequest& operator=(const InstallPolicyESimRequest&) =
delete;
~InstallPolicyESimRequest();
const policy_util::SmdxActivationCode activation_code;
base::Value::Dict onc_config;
net::BackoffEntry retry_backoff;
};
// HermesManagerClient::Observer:
void OnAvailableEuiccListChanged() override;
// CellularESimProfileHandler::Observer:
void OnESimProfileListUpdated() override;
// NetworkStateHandlerObserver:
void DeviceListChanged() override;
void OnShuttingDown() override;
// These functions implement the functionality necessary to interact with the
// queue of policy eSIM installation requests.
void ResumeInstallIfNeeded();
void ProcessRequests();
void ScheduleRetryAndProcessRequests(
std::unique_ptr<InstallPolicyESimRequest> request,
InstallRetryReason reason);
void PushRequestAndProcess(std::unique_ptr<InstallPolicyESimRequest> request);
void PopRequest();
void PopAndProcessRequests();
// Attempts to install the first request in the queue. This function is
// responsible for ensuring that both a cellular device and Hermes are
// available. Further, this function will also refresh the list of installed
// eSIM profiles so that we can properly determine whether an eSIM profile has
// already been installed for the request.
void AttemptInstallESim();
// Actually responsible for kicking off the installation process. This
// function will configure the Shill service that corresponds to the profile
// that will be installed, and will ensure that we have non-cellular internet
// connectivity.
void PerformInstallESim(const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties);
void OnRefreshProfileList(
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
void OnConfigureESimService(std::optional<dbus::ObjectPath> service_path);
void OnInhibitedForRefreshSmdxProfiles(
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
void OnRefreshSmdxProfiles(
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
base::TimeTicks start_time,
HermesResponseStatus status,
const std::vector<dbus::ObjectPath>& profile_paths);
void CompleteRefreshSmdxProfiles(
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
HermesResponseStatus status,
const std::vector<dbus::ObjectPath>& profile_paths);
void OnESimProfileInstallAttemptComplete(
HermesResponseStatus hermes_status,
std::optional<dbus::ObjectPath> profile_path,
std::optional<std::string> service_path);
void OnWaitTimeout();
base::Value::Dict GetNewShillProperties();
const policy_util::SmdxActivationCode& GetCurrentActivationCode() const;
std::optional<dbus::ObjectPath> FindExistingMatchingESimProfile(
const std::string& iccid);
// Return std::nullopt if no or empty iccid is found in the policy ONC.
std::optional<std::string> GetIccidFromPolicyONC();
bool HasNonCellularInternetConnectivity();
InstallRetryReason HermesResponseStatusToRetryReason(
HermesResponseStatus status) const;
raw_ptr<CellularESimProfileHandler> cellular_esim_profile_handler_ = nullptr;
raw_ptr<CellularESimInstaller> cellular_esim_installer_ = nullptr;
raw_ptr<CellularInhibitor> cellular_inhibitor_ = nullptr;
raw_ptr<NetworkProfileHandler> network_profile_handler_ = nullptr;
raw_ptr<NetworkStateHandler> network_state_handler_ = nullptr;
base::ScopedObservation<NetworkStateHandler, NetworkStateHandlerObserver>
network_state_handler_observer_{this};
raw_ptr<ManagedCellularPrefHandler, DanglingUntriaged>
managed_cellular_pref_handler_ = nullptr;
raw_ptr<ManagedNetworkConfigurationHandler, DanglingUntriaged>
managed_network_configuration_handler_ = nullptr;
bool is_installing_ = false;
// While Hermes is the source of truth for the EUICC state, Chrome maintains a
// cache of the installed eSIM profiles. To ensure we properly detect when a
// profile has already been installed for a particular request we force a
// refresh of the profile cache before each installation.
bool need_refresh_profile_list_ = true;
base::circular_deque<std::unique_ptr<InstallPolicyESimRequest>>
remaining_install_requests_;
base::OneShotTimer wait_timer_;
base::ScopedObservation<HermesManagerClient, HermesManagerClient::Observer>
hermes_observation_{this};
base::ScopedObservation<CellularESimProfileHandler,
CellularESimProfileHandler::Observer>
cellular_esim_profile_handler_observation_{this};
base::WeakPtrFactory<CellularPolicyHandler> weak_ptr_factory_{this};
};
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_POLICY_HANDLER_H_
|