// 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_ESIM_INSTALLER_H_
#define CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_ESIM_INSTALLER_H_
#include <map>
#include "base/component_export.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
#include "chromeos/ash/components/network/cellular_esim_profile_handler.h"
#include "chromeos/ash/components/network/cellular_inhibitor.h"
#include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "dbus/dbus_result.h"
namespace dbus {
class ObjectPath;
} // namespace dbus
namespace ash {
class CellularConnectionHandler;
class NetworkConnectionHandler;
class NetworkProfileHandler;
class NetworkStateHandler;
// Handles installation of an eSIM profile and its corresponding network.
//
// Installing an eSIM profile involves the following operations:
// 1. Inhibit cellular scans.
// 2. Install eSIM profile in Hermes with activation code.
// 3. Create cellular Shill service configuration.
// 4. Prepare newly installed cellular network for connection (ie. profile
// enable).
// 5. Connect to network with the new profile.
class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularESimInstaller {
public:
CellularESimInstaller();
CellularESimInstaller(const CellularESimInstaller&) = delete;
CellularESimInstaller& operator=(const CellularESimInstaller&) = delete;
~CellularESimInstaller();
void Init(CellularConnectionHandler* cellular_connection_handler,
CellularInhibitor* cellular_inhibitor,
NetworkConnectionHandler* network_connection_handler,
NetworkProfileHandler* network_profile_handler,
NetworkStateHandler* network_state_handler);
// Return callback for the InstallProfileFromActivationCode method.
// |hermes_status| is the status of the eSIM installation.
// |profile_path| is the path to the newly installed eSIM profile
// and |service_path| is the path to the corresponding network service.
// |profile_path| and |service_path| will be std::nullopt on error.
using InstallProfileFromActivationCodeCallback =
base::OnceCallback<void(HermesResponseStatus hermes_status,
std::optional<dbus::ObjectPath> profile_path,
std::optional<std::string> service_path)>;
// Return callback for the ConfigureESimService method. |service_path|
// is the path of the newly configured eSIM service. A nullopt |service_path|
// indicates failure.
using ConfigureESimServiceCallback =
base::OnceCallback<void(std::optional<dbus::ObjectPath> service_path)>;
// Installs an ESim profile and network with given |activation_code|,
// |confirmation_code| and |euicc_path|. This method will attempt to create
// a Shill configuration with the given |new_shill_properties|, and and will
// enable and attempt to connect to the installed profile afterward. Both
// |is_initial_install| and |install_method| are used for recording eSIM
// policy installation metrics.
void InstallProfileFromActivationCode(
const std::string& activation_code,
const std::string& confirmation_code,
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
InstallProfileFromActivationCodeCallback callback,
bool is_initial_install,
cellular_setup::mojom::ProfileInstallMethod install_method);
// Attempts to create a Shill service configuration with given
// |new_shill_properties| for eSIM with |profile_path| and |euicc_path|.
// |callback| is called with the newly configure service path.
void ConfigureESimService(const base::Value::Dict& new_shill_properties,
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& profile_path,
ConfigureESimServiceCallback callback);
private:
friend class CellularESimInstallerTest;
friend class CellularPolicyHandlerTest;
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest,
InstallProfileInvalidActivationCode);
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest,
InstallProfileConnectFailure);
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest, InstallProfileSuccess);
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest,
InstallProfileAutoConnect);
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest,
InstallProfileViaQrCodeSuccess);
FRIEND_TEST_ALL_PREFIXES(CellularESimInstallerTest,
InstallProfileAlreadyConnected);
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class InstallESimProfileResult {
kSuccess = 0,
kInhibitFailed = 1,
kHermesInstallFailed = 2,
kMaxValue = kHermesInstallFailed
};
// Record the result of an attempt to install an eSIM profile. This function
// will emit to histograms that capture the method used and whether this is
// the first installation attempt or not. When |status| is not provided this
// function assumes that we failed to inhibit the cellular device.
static void RecordInstallESimProfileResult(
std::optional<HermesResponseStatus> status,
bool is_managed,
bool is_initial_install,
cellular_setup::mojom::ProfileInstallMethod install_method);
void PerformInstallProfileFromActivationCode(
const std::string& activation_code,
const std::string& confirmation_code,
const dbus::ObjectPath& euicc_path,
base::Value::Dict new_shill_properties,
bool is_initial_install,
cellular_setup::mojom::ProfileInstallMethod install_method,
InstallProfileFromActivationCodeCallback callback,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
void OnProfileInstallResult(
InstallProfileFromActivationCodeCallback callback,
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
const dbus::ObjectPath& euicc_path,
const base::Value::Dict& new_shill_properties,
bool is_initial_install,
cellular_setup::mojom::ProfileInstallMethod install_method,
const base::Time installation_start_time,
HermesResponseStatus status,
dbus::DBusResult dbus_result,
const dbus::ObjectPath* object_path);
void OnShillConfigurationCreationSuccess(
ConfigureESimServiceCallback callback,
const dbus::ObjectPath& service_path);
void OnShillConfigurationCreationFailure(
ConfigureESimServiceCallback callback,
const std::string& error_name,
const std::string& error_message);
void EnableProfile(InstallProfileFromActivationCodeCallback callback,
const dbus::ObjectPath& euicc_path,
const dbus::ObjectPath& profile_path,
std::optional<dbus::ObjectPath> service_path);
void OnPrepareCellularNetworkForConnectionSuccess(
const dbus::ObjectPath& profile_path,
InstallProfileFromActivationCodeCallback callback,
const std::string& service_path,
bool auto_connected);
void OnPrepareCellularNetworkForConnectionFailure(
const dbus::ObjectPath& profile_path,
InstallProfileFromActivationCodeCallback callback,
const std::string& service_path,
const std::string& error_name);
void HandleNewProfileEnableFailure(
InstallProfileFromActivationCodeCallback callback,
const dbus::ObjectPath& profile_path,
const std::string& service_path,
const std::string& error_name);
raw_ptr<CellularConnectionHandler, DanglingUntriaged>
cellular_connection_handler_;
raw_ptr<CellularInhibitor> cellular_inhibitor_;
raw_ptr<NetworkConnectionHandler, DanglingUntriaged>
network_connection_handler_;
raw_ptr<NetworkProfileHandler, DanglingUntriaged> network_profile_handler_;
raw_ptr<NetworkStateHandler> network_state_handler_;
// Maps profile dbus paths to unique pointer of InhibitLocks that are
// pending to uninhibit.
std::map<dbus::ObjectPath, std::unique_ptr<CellularInhibitor::InhibitLock>>
pending_inhibit_locks_;
base::WeakPtrFactory<CellularESimInstaller> weak_ptr_factory_{this};
};
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_ESIM_INSTALLER_H_