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
|
// 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_CONNECTION_HANDLER_H_
#define CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_CONNECTION_HANDLER_H_
#include <memory>
#include <optional>
#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 "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
#include "chromeos/ash/components/network/cellular_inhibitor.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "dbus/object_path.h"
namespace ash {
namespace cellular_setup {
class ESimTestBase;
}
class CellularESimProfileHandler;
class CellularInhibitor;
class NetworkState;
// Prepares cellular networks for connection. Before we can connect to a
// cellular network, the network must be backed by Shill and must have its
// Connectable property set to true (meaning that it is the selected SIM
// profile in its slot).
//
// For pSIM networks, Chrome OS only supports a single physical SIM slot, so
// pSIM networks should always have their Connectable properties set to true as
// long as they are backed by Shill. Shill is expected to create a Service for
// each pSIM, so the only thing that needs to be done is to wait for that pSIM
// Service to be created by Shill.
//
// For eSIM networks, it is possible that there are multiple eSIM profiles on a
// single EUICC; in this case, Connectable == false refers to a disabled eSIM
// profile which must be enabled via Hermes before a connection can succeed. The
// steps for an eSIM network are:
// (1) Check to see if the profile is already enabled; if so, return early.
// (2) Inhibit cellular scans.
// (3) Request installed profiles from Hermes.
// (4) Enable the relevant profile.
// (5) Uninhibit cellular scans.
// (6) Wait until the associated NetworkState becomes connectable.
// (7) Wait until Shill auto connected if the sim slot is switched.
//
// Note that if this class receives multiple connection requests, it processes
// them in FIFO order.
class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularConnectionHandler
: public NetworkStateHandlerObserver {
public:
// TODO(b/271854446): Make these private once the migration has landed.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class PrepareCellularConnectionResult {
kSuccess = 0,
kCouldNotFindNetworkWithIccid = 1,
kInhibitFailed = 2,
kCouldNotFindRelevantEuicc = 3,
kRefreshProfilesFailed = 4,
kCouldNotFindRelevantESimProfile = 5,
kEnableProfileFailed = 6,
kTimeoutWaitingForConnectable = 7,
kSimLocked = 8,
kMaxValue = kSimLocked
};
CellularConnectionHandler();
CellularConnectionHandler(const CellularConnectionHandler&) = delete;
CellularConnectionHandler& operator=(const CellularConnectionHandler&) =
delete;
~CellularConnectionHandler() override;
void Init(NetworkStateHandler* network_state_handler,
CellularInhibitor* cellular_inhibitor,
CellularESimProfileHandler* cellular_esim_profile_handler);
// Success callback which receives the network's service path as the first
// parameter and a boolean indicates whether the network is autoconnected
// as the second parameter.
typedef base::OnceCallback<void(const std::string&, bool)> SuccessCallback;
// Error callback which receives the network's service path as the first
// parameter and an error name as the second parameter. If no service path is
// available (e.g., if no network with the given ICCID was found), an empty
// string is passed as the first parameter.
typedef base::OnceCallback<void(const std::string&, const std::string&)>
ErrorCallback;
// Prepares an existing network (i.e., one which has *not* just been
// installed) for a connection. Upon success, the network will be backed by
// Shill and will be connectable.
void PrepareExistingCellularNetworkForConnection(
const std::string& iccid,
SuccessCallback success_callback,
ErrorCallback error_callback);
// Prepares a newly-installed eSIM profile for connection. This should be
// called immediately after installation succeeds so that the profile is
// enabled in Hermes. Upon success, the network will be backed by Shill and
// will be connectable.
void PrepareNewlyInstalledCellularNetworkForConnection(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& profile_path,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
SuccessCallback success_callback,
ErrorCallback error_callback);
private:
friend class CellularESimInstallerTest;
friend class CellularPolicyHandlerTest;
friend class ManagedNetworkConfigurationHandlerTest;
friend class cellular_setup::ESimTestBase;
struct ConnectionRequestMetadata {
ConnectionRequestMetadata(const std::string& iccid,
SuccessCallback success_callback,
ErrorCallback error_callback);
ConnectionRequestMetadata(
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& profile_path,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
SuccessCallback success_callback,
ErrorCallback error_callback);
~ConnectionRequestMetadata();
std::optional<std::string> iccid;
std::optional<dbus::ObjectPath> euicc_path;
std::optional<dbus::ObjectPath> profile_path;
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
// A boolean indicating that if the connection switches the SIM profile and
// requires enabling the profile first.
bool did_connection_require_enabling_profile = false;
SuccessCallback success_callback;
ErrorCallback error_callback;
};
enum class ConnectionState {
kIdle,
kCheckingServiceStatus,
kInhibitingScans,
kRequestingProfilesBeforeEnabling,
kEnablingProfile,
kWaitingForConnectable,
kWaitingForShillAutoConnect,
};
friend std::ostream& operator<<(std::ostream& stream,
const ConnectionState& step);
// Timeout waiting for a cellular network to auto connect after switch
// profile.
static const base::TimeDelta kWaitingForAutoConnectTimeout;
static std::optional<std::string> ResultToErrorString(
PrepareCellularConnectionResult result);
// NetworkStateHandlerObserver:
void NetworkListChanged() override;
void NetworkPropertiesUpdated(const NetworkState* network) override;
void NetworkIdentifierTransitioned(const std::string& old_service_path,
const std::string& new_service_path,
const std::string& old_guid,
const std::string& new_guid) override;
void NetworkConnectionStateChanged(const NetworkState* network) override;
void DevicePropertiesUpdated(const DeviceState* device) override;
void ProcessRequestQueue();
void TransitionToConnectionState(ConnectionState state);
// Invokes the success or error callback, depending on |result| and
// |auto_connected|.
void CompleteConnectionAttempt(PrepareCellularConnectionResult result,
bool auto_connected);
const NetworkState* GetNetworkStateForCurrentOperation() const;
std::optional<dbus::ObjectPath> GetEuiccPathForCurrentOperation() const;
std::optional<dbus::ObjectPath> GetProfilePathForCurrentOperation() const;
void CheckServiceStatus();
void OnInhibitScanResult(
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
void RequestInstalledProfiles();
void OnRefreshProfileListResult(
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
void EnableProfile();
void OnEnableCarrierProfileResult(HermesResponseStatus status);
void UninhibitScans(const std::optional<std::string>& error_before_uninhibit);
void OnUninhibitScanResult(
const std::optional<std::string>& error_before_uninhibit,
bool success);
void HandleNetworkPropertiesUpdate();
void CheckForConnectable();
void OnWaitForConnectableTimeout();
void StartWaitingForShillAutoConnect();
void CheckForAutoConnected();
void OnWaitForAutoConnectTimeout();
base::OneShotTimer timer_;
raw_ptr<NetworkStateHandler> network_state_handler_ = nullptr;
base::ScopedObservation<NetworkStateHandler, NetworkStateHandlerObserver>
network_state_handler_observer_{this};
raw_ptr<CellularInhibitor> cellular_inhibitor_ = nullptr;
raw_ptr<CellularESimProfileHandler, DanglingUntriaged>
cellular_esim_profile_handler_ = nullptr;
ConnectionState state_ = ConnectionState::kIdle;
base::queue<std::unique_ptr<ConnectionRequestMetadata>> request_queue_;
base::WeakPtrFactory<CellularConnectionHandler> weak_ptr_factory_{this};
};
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_CONNECTION_HANDLER_H_
|