chromium/chromeos/ash/components/dbus/hermes/hermes_euicc_client.h

// 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.

#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_HERMES_HERMES_EUICC_CLIENT_H_
#define CHROMEOS_ASH_COMPONENTS_DBUS_HERMES_HERMES_EUICC_CLIENT_H_

#include <string>
#include <vector>

#include "ash/constants/ash_features.h"
#include "base/check.h"
#include "base/functional/callback_forward.h"
#include "base/observer_list.h"
#include "chromeos/ash/components/dbus/hermes/hermes_response_status.h"
#include "chromeos/dbus/common/dbus_callback.h"
#include "dbus/dbus_result.h"
#include "dbus/property.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"

namespace ash {

// HermesEuiccClient is used to talk to the Hermes Euicc objects.
class COMPONENT_EXPORT(HERMES_CLIENT) HermesEuiccClient {
 public:
  // Callback for profile installation methods. Callback returns status code
  // and the object path for the profile that was just successfully installed.
  using InstallCarrierProfileCallback =
      base::OnceCallback<void(HermesResponseStatus status,
                              dbus::DBusResult result,
                              const dbus::ObjectPath* carrier_profile_path)>;

  // Callback for the RefreshSmdxProfiles(). Callback returns the status code
  // and 0 or more object paths for profiles available to be installed for the
  // activation code provided to the method.
  using RefreshSmdxProfilesCallback = base::OnceCallback<void(
      HermesResponseStatus status,
      const std::vector<dbus::ObjectPath>& profile_paths)>;

  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  //
  // LINT.IfChange(InstallationAttemptStep)
  enum class InstallationAttemptStep {
    kInstallationRequested = 0,
    kHermesUnavailable = 1,
    kInstallationStarted = 2,
    kInstallationSucceeded = 3,
    kInstallationNoResponse = 4,
    kInstallationFailed = 5,
    kMaxValue = kInstallationFailed
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/network/enums.xml:InstallationAttemptStep)

  class TestInterface {
   public:
    enum class AddCarrierProfileBehavior {
      // Adds a profile which can be immediately retrieved from a EUICC by
      // examining its installed profiles. Also creates a Shill service
      // representing the profile.
      kAddProfileWithService,

      // Adds a profile which is not yet present of the EUICC's properties but
      // will be available after a subsequent call to
      // RefreshInstalledProfiles(). Also creates a Shill service representing
      // the profile.
      kAddDelayedProfileWithService,

      // Adds a profile which can be immediately retrieved from a EUICC by
      // examining its installed profiles. Does not create an accompanying Shill
      // service representing the profile, which simulates a "stub" cellular
      // service.
      kAddProfileWithoutService,

      // Adds a profile which is not yet present of the EUICC's properties but
      // will be available after a subsequent call to
      // RefreshInstalledProfiles(). Does not create an accompanying Shill
      // service representing the profile, which simulates a "stub" cellular
      // service.
      kAddDelayedProfileWithoutService,
    };

    // Clears a given Euicc and associated profiles.
    virtual void ClearEuicc(const dbus::ObjectPath& euicc_path) = 0;

    // Resets the test interface for a new fake pending profile
    // will be added on subsequent  call to RequestPendingEvents.
    virtual void ResetPendingEventsRequested() = 0;

    // Adds a new carrier profile under given euicc object using fake default
    // values for properties. If |state| is not pending then a corresponding
    // fake cellular service is also created in shill. The
    // |add_carrier_profile_behavior| parameter determines whether an associated
    // Shill service is created as well as whether the profile is added to the
    // EUICC immediately or only after RefreshInstalledProfiles() is called.
    // Returns the path to the newly added profile.
    virtual dbus::ObjectPath AddFakeCarrierProfile(
        const dbus::ObjectPath& euicc_path,
        hermes::profile::State state,
        const std::string& activation_code,
        AddCarrierProfileBehavior add_carrier_profile_behavior) = 0;

    // Adds a new carrier profile with given path and properties.
    virtual void 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) = 0;

    // Remove a carrier profile with path |carrier_profile_path| from EUICC with
    // given |euicc_path|. Return true if successful.
    virtual bool RemoveCarrierProfile(
        const dbus::ObjectPath& euicc_path,
        const dbus::ObjectPath& carrier_profile_path) = 0;

    // Updates the SIM slot information cached by Shill to match Hermes' state.
    virtual void UpdateShillDeviceSimSlotInfo() = 0;

    // Queues an error code that will be returned from a subsequent
    // method call.
    virtual void QueueHermesErrorStatus(HermesResponseStatus status) = 0;

    // Sets the return for the next call to
    // HermesEuiccClient::InstallProfileFromActivationCode(). The implementation
    // of this method should only accept error statuses since clients expect
    // addition information about the installed profile on success.
    virtual void SetNextInstallProfileFromActivationCodeResult(
        HermesResponseStatus status) = 0;

    // Sets the return for the next call to
    // HermesEuiccClient::RefreshSmdxProfiles(). The caller is responsible for
    // guaranteeing that fake profiles exist for each of the paths provided.
    virtual void SetNextRefreshSmdxProfilesResult(
        std::vector<dbus::ObjectPath> profiles) = 0;

    // Set delay for interactive methods.
    virtual void SetInteractiveDelay(base::TimeDelta delay) = 0;

    // Returns a valid fake activation code that can be used to install
    // a new fake carrier profile.
    virtual std::string GenerateFakeActivationCode() = 0;

    // Returns an activation code that will trigger no memory error from DBUS
    // upon attempts to activate it.
    virtual std::string GetDBusErrorActivationCode() = 0;

    // Returns true when the last call to RefreshInstalledProfiles was requested
    // with |restore_slot| set to true.
    virtual bool GetLastRefreshProfilesRestoreSlotArg() = 0;
  };

  // Hermes Euicc properties.
  class Properties : public dbus::PropertySet {
   public:
    Properties(dbus::ObjectProxy* object_proxy,
               const PropertyChangedCallback& callback);
    ~Properties() override;

    dbus::Property<std::string>& eid() { return eid_; }
    dbus::Property<bool>& is_active() { return is_active_; }
    dbus::Property<std::vector<dbus::ObjectPath>>& profiles() {
      return profiles_;
    }
    dbus::Property<int32_t>& physical_slot() { return physical_slot_; }

   private:
    // EID of the Euicc.
    dbus::Property<std::string> eid_;

    // Boolean that indicates whether this euicc is currently active.
    dbus::Property<bool> is_active_;

    // List of all carrier profiles known to the device. This includes
    // currently installed profiles and pending profiles scanned from
    // SM-DS or SM-DP+ servers.
    dbus::Property<std::vector<dbus::ObjectPath>> profiles_;

    // Physical slot number of the Euicc.
    dbus::Property<int32_t> physical_slot_;
  };

  // Interface for observing Hermes Euicc changes.
  class Observer {
   public:
    virtual ~Observer() = default;

    // Called when an euicc property changes.
    virtual void OnEuiccPropertyChanged(const dbus::ObjectPath& euicc_path,
                                        const std::string& property_name) {}

    // Called when an Euicc reset operation completes successfully.
    virtual void OnEuiccReset(const dbus::ObjectPath& euicc_path) {}
  };

  static constexpr char kHermesInstallationAttemptStepsHistogram[] =
      "Network.Ash.Cellular.ESim.HermesInstallationAttempt.Step";

  // Adds an observer for carrier profile lists changes on Hermes manager.
  virtual void AddObserver(Observer* observer);

  // Removes an observer for Hermes manager.
  virtual void RemoveObserver(Observer* observer);

  // Install a carrier profile in the Euicc at |euicc_path| with given
  // |activation_code| and |conirmation_code|. |confirmation_code| can be empty
  // if no confirmation is required by carrier. Returns the object path to the
  // carrier profile that was just installed.
  virtual void InstallProfileFromActivationCode(
      const dbus::ObjectPath& euicc_path,
      const std::string& activation_code,
      const std::string& confirmation_code,
      InstallCarrierProfileCallback callback) = 0;

  // Installs a pending profile with given |carrier_profile_path| in the Euicc
  // at |euicc_path|. |confirmation_code| can be empty if no confirmation is
  // required by carrier. Returns a response status indicating the install
  // result.
  virtual void InstallPendingProfile(
      const dbus::ObjectPath& euicc_path,
      const dbus::ObjectPath& carrier_profile_path,
      const std::string& confirmation_code,
      HermesResponseCallback callback) = 0;

  // Refreshes installed profiles for Euicc at |euicc_path|.
  // This updates installed profiles list prior to returning.
  // If |restore_slot| is true then SIM slot that was active prior to refreshing
  // is restored.
  virtual void RefreshInstalledProfiles(const dbus::ObjectPath& euicc_path,
                                        bool restore_slot,
                                        HermesResponseCallback callback) = 0;

  // Fetches the available profiles for Euicc at |euicc_path| using the
  // activation code provided by |activation_code|. This method will update the
  // set of known profiles before returning. If |restore_slot| is |true| then
  // the SIM slot that was active prior to refreshing is restored.
  virtual void RefreshSmdxProfiles(const dbus::ObjectPath& euicc_path,
                                   const std::string& activation_code,
                                   bool restore_slot,
                                   RefreshSmdxProfilesCallback callback) = 0;

  // Updates pending profiles for Euicc at |euicc_path| from the SMDS server
  // using the given |root_smds| server address. Passing an empty |root_smds|
  // will use default lpa.ds.gsma.com. This updates pending profiles list prior
  // to returning.
  virtual void RequestPendingProfiles(const dbus::ObjectPath& euicc_path,
                                      const std::string& root_smds,
                                      HermesResponseCallback callback) = 0;

  // Removes the carrier profile with the given |carrier_profile_path| from
  // the Euicc at |euicc_path|. Returns a response status indicating the result
  // of the operation.
  virtual void UninstallProfile(const dbus::ObjectPath& euicc_path,
                                const dbus::ObjectPath& carrier_profile_path,
                                HermesResponseCallback callback) = 0;

  // Erases all profiles on the Euicc at |euicc_path|. |reset_option| specifies
  // the type of reset operation that will be performed.
  virtual void ResetMemory(const dbus::ObjectPath& euicc_path,
                           hermes::euicc::ResetOptions reset_option,
                           HermesResponseCallback callback) = 0;

  // Returns properties for the Euicc with given |euicc_path|.
  virtual Properties* GetProperties(const dbus::ObjectPath& euicc_path) = 0;

  // Returns an instance of Hermes Euicc Test interface.
  virtual TestInterface* GetTestInterface() = 0;

  // Creates and initializes the global instance.
  static void Initialize(dbus::Bus* bus);

  // Creates and initializes a global fake instance.
  static void InitializeFake();

  // Destroys the global instance.
  static void Shutdown();

  // Returns the global instance.
  static HermesEuiccClient* Get();

 protected:
  HermesEuiccClient();
  virtual ~HermesEuiccClient();

  const base::ObserverList<Observer>::Unchecked& observers() {
    return observers_;
  }

 private:
  friend class HermesEuiccClientTest;
  friend class HermesEuiccClientImpl;

  base::ObserverList<Observer>::Unchecked observers_;
  static constexpr base::TimeDelta kInstallRetryDelay = base::Seconds(3);
  static const int kMaxInstallAttempts = 4;
};

}  // namespace ash

#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_HERMES_HERMES_EUICC_CLIENT_H_