chromium/chromeos/ash/components/network/cellular_policy_handler_unittest.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/network/cellular_policy_handler.h"

#include <memory>
#include <optional>
#include <queue>

#include "ash/constants/ash_features.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/dbus/hermes/hermes_clients.h"
#include "chromeos/ash/components/network/cellular_esim_installer.h"
#include "chromeos/ash/components/network/cellular_inhibitor.h"
#include "chromeos/ash/components/network/cellular_utils.h"
#include "chromeos/ash/components/network/managed_cellular_pref_handler.h"
#include "chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h"
#include "chromeos/ash/components/network/metrics/cellular_network_metrics_test_helper.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/technology_state_controller.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/onc/onc_constants.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/hermes/dbus-constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {

using InhibitReason = CellularInhibitor::InhibitReason;

namespace {

// EUICC constants
const char kTestEuiccPath0[] = "/org/chromium/Hermes/Euicc/0";
const char kTestEuiccPath1[] = "/org/chromium/Hermes/Euicc/1";
const char kTestEid0[] = "00000000000000000000000000000000";
const char kTestEid1[] = "11111111111111111111111111111111";

// Profile constants
const char kTestProfilePath0[] =
    "/org/chromium/Hermes/Euicc/0/Profile/0000000000000000000";
const char kTestProfileIccid0[] = "0000000000000000000";
const char kTestProfileName0[] = "Name";
const char kTestProfileNickname0[] = "Nickname";
const char kTestProfileServiceProvider0[] = "Service Provider";

// Shill constants
const char kTestCellularDevicePath[] = "/device/cellular";
const char kTestCellularDevicePathName[] = "stub_cellular_device";
const char kTestProfileServicePath0[] = "/service/profile0";

const char kCellularPolicyPattern[] =
    R"({
      "GUID": "Cellular-%lu",
      "Type": "Cellular",
      "Name": "Cellular",
      "Cellular": %s
    })";
const char kCellularPolicyCellularTypePattern[] =
    R"({
      "%s": "%s"
    })";
const char kCellularPolicyCellularTypeWithIccidPattern[] =
    R"({
      "%s": "%s",
      "ICCID": "%s"
    })";


std::string GenerateCellularPolicy(
    const policy_util::SmdxActivationCode& activation_code,
    std::optional<std::string> iccid = std::nullopt) {
  const char* const activation_code_type =
      activation_code.type() == policy_util::SmdxActivationCode::Type::SMDP
          ? onc::cellular::kSMDPAddress
          : onc::cellular::kSMDSAddress;
  const std::string cellular_type =
      iccid.has_value()
          ? base::StringPrintf(kCellularPolicyCellularTypeWithIccidPattern,
                               activation_code_type,
                               activation_code.value().c_str(), iccid->c_str())
          : base::StringPrintf(kCellularPolicyCellularTypePattern,
                               activation_code_type,
                               activation_code.value().c_str());
  return base::StringPrintf(kCellularPolicyPattern, base::RandUint64(),
                            cellular_type.c_str());
}

}  // namespace

class CellularInhibitorObserver : public CellularInhibitor::Observer {
 public:
  CellularInhibitorObserver() {
    session_observation_.Observe(NetworkHandler::Get()->cellular_inhibitor());
  }

  void OnInhibitStateChanged() override {
    std::optional<InhibitReason> inhibit_reason =
        NetworkHandler::Get()->cellular_inhibitor()->GetInhibitReason();
    if (inhibit_reason.has_value()) {
      inhibit_reasons_.push(*inhibit_reason);
    }
  }

  std::optional<InhibitReason> PopInhibitReason() {
    std::optional<InhibitReason> inhibit_reason;
    if (!inhibit_reasons_.empty()) {
      inhibit_reason = inhibit_reasons_.front();
      inhibit_reasons_.pop();
    }
    return inhibit_reason;
  }

 private:
  std::queue<InhibitReason> inhibit_reasons_;
  base::ScopedObservation<CellularInhibitor, CellularInhibitor::Observer>
      session_observation_{this};
};

class CellularPolicyHandlerTest : public testing::Test {
 protected:
  // This struct is used to simplify checking the metrics associated with the
  // tests below.
  struct ExpectedHistogramState {
    size_t success_initial_count = 0u;
    size_t success_retry_count = 0u;
    size_t inhibit_failed_initial_count = 0u;
    size_t inhibit_failed_retry_count = 0u;
    size_t hermes_install_failed_initial_count = 0u;
    size_t hermes_install_failed_retry_count = 0u;
    size_t smds_scan_profile_total_count = 0u;
    size_t smds_scan_profile_sum = 0u;
    size_t no_available_profiles_via_smdp_count = 0u;
    size_t no_available_profiles_via_smds_count = 0u;
    size_t install_method_via_smdp_count = 0u;
    size_t install_method_via_smds_count = 0u;
    size_t scan_duration_other_success_count = 0u;
    size_t scan_duration_other_failure_count = 0u;
    size_t scan_duration_android_success_count = 0u;
    size_t scan_duration_android_failure_count = 0u;
    size_t scan_duration_gsma_success_count = 0u;
    size_t scan_duration_gsma_failure_count = 0u;
    cellular_metrics::ESimSmdsScanHistogramState smds_scan_state;
  };

  CellularPolicyHandlerTest() {}
  ~CellularPolicyHandlerTest() override = default;

  void SetUp() override {
    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
    network_handler_test_helper_->ResetDevicesAndServices();
    network_handler_test_helper_->RegisterPrefs(profile_prefs_.registry(),
                                                device_prefs_.registry());
    network_handler_test_helper_->InitializePrefs(&profile_prefs_,
                                                  &device_prefs_);
    cellular_policy_handler_ = NetworkHandler::Get()->cellular_policy_handler();
    base::RunLoop().RunUntilIdle();

    cellular_policy_handler_ = NetworkHandler::Get()->cellular_policy_handler();
  }

  void TearDown() override { network_handler_test_helper_.reset(); }

  // There are multiple dependencies/requirements for installing an eSIM
  // profile. This function wraps all of the setup into a single method for
  // convenience.
  void SetupGolden() {
    AddCellularDevice();
    AddEuiccs();
    AddWiFi();
  }

  void AddCellularDevice() {
    network_handler_test_helper_->device_test()->AddDevice(
        kTestCellularDevicePath, shill::kTypeCellular,
        kTestCellularDevicePathName);
    base::RunLoop().RunUntilIdle();
  }

  void AddEuiccs() {
    HermesManagerClient::Get()->GetTestInterface()->ClearEuiccs();

    // We call FastForwardRefreshDelay() after each time we add an EUICC since
    // adding an EUICC will trigger an attempt to refresh/request the list of
    // installed profiles.
    HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
        dbus::ObjectPath(kTestEuiccPath0), kTestEid0, /*is_active=*/true,
        /*physical_slot=*/0);
    FastForwardRefreshDelay();
    base::RunLoop().RunUntilIdle();

    HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
        dbus::ObjectPath(kTestEuiccPath1), kTestEid1, /*is_active=*/false,
        /*physical_slot=*/1);
    FastForwardRefreshDelay();
    base::RunLoop().RunUntilIdle();
  }

  void AddWiFi() {
    network_handler_test_helper_->ConfigureWiFi(shill::kStateOnline);
    base::RunLoop().RunUntilIdle();
  }

  void InstallProfile(const base::Value::Dict& onc_config) {
    cellular_policy_handler()->InstallESim(onc_config);
    base::RunLoop().RunUntilIdle();

    FastForwardRefreshDelay();
  }

  HermesProfileClient::Properties* FindProfileProperties(
      const std::string& activation_code_value) {
    std::optional<dbus::ObjectPath> euicc_path =
        cellular_utils::GetCurrentEuiccPath();
    if (!euicc_path.has_value()) {
      return nullptr;
    }

    HermesEuiccClient::Properties* euicc_properties =
        HermesEuiccClient::Get()->GetProperties(*euicc_path);
    if (!euicc_properties) {
      return nullptr;
    }

    for (auto profile : euicc_properties->profiles().value()) {
      HermesProfileClient::Properties* profile_properties =
          HermesProfileClient::Get()->GetProperties(profile);
      if (profile_properties && profile_properties->activation_code().value() ==
                                    activation_code_value) {
        return profile_properties;
      }
    }
    return nullptr;
  }

  bool IsProfileInstalled(const base::Value::Dict& onc_config,
                          const std::string& activation_code_value,
                          bool check_for_service) {
    HermesProfileClient::Properties* profile_properties =
        FindProfileProperties(activation_code_value);
    if (!profile_properties) {
      LOG(INFO) << "Failed to find Hermes profile properties";
      return false;
    }

    if (profile_properties->state().value() ==
        hermes::profile::State::kPending) {
      LOG(INFO) << "Hermes profile is in the pending state";
      return false;
    }

    const std::string* guid =
        onc_config.FindString(::onc::network_config::kGUID);
    const std::string& iccid = profile_properties->iccid().value();
    if (iccid.empty() || !guid || guid->empty()) {
      LOG(INFO) << "Missing ICCID or GUID";
      return false;
    }

    return !check_for_service || HasShillConfiguration(*guid, iccid);
  }

  bool HasShillConfiguration(const std::string& guid,
                             const std::string& iccid) {
    const std::string shill_service_path =
        ShillServiceClient::Get()->GetTestInterface()->FindServiceMatchingGUID(
            guid);
    const base::Value::Dict* properties =
        ShillServiceClient::Get()->GetTestInterface()->GetServiceProperties(
            shill_service_path);

    if (!properties) {
      LOG(INFO) << "Failed to find Shill service properties";
      return false;
    }

    const std::string* shill_guid =
        properties->FindString(shill::kGuidProperty);
    if (!shill_guid || guid != *shill_guid) {
      LOG(INFO) << "Missing or mismatched GUID";
      return false;
    }

    const std::string* shill_iccid =
        properties->FindString(shill::kIccidProperty);
    if (!shill_iccid || iccid != *shill_iccid) {
      LOG(INFO) << "Missing or mismatched ICCID";
      return false;
    }

    // UI data should not be empty for configured cellular services.
    return properties->FindString(shill::kUIDataProperty);
  }

  bool HasESimMetadata(const std::string& activation_code_value) {
    HermesProfileClient::Properties* profile_properties =
        FindProfileProperties(activation_code_value);
    if (!profile_properties) {
      LOG(INFO) << "Failed to find Hermes profile properties";
      return false;
    }
    return NetworkHandler::Get()
               ->managed_cellular_pref_handler()
               ->GetESimMetadata(profile_properties->iccid().value()) !=
           nullptr;
  }

  void CheckCurrentEuiccSlot(int32_t physical_slot) {
    std::optional<dbus::ObjectPath> euicc_path =
        cellular_utils::GetCurrentEuiccPath();
    ASSERT_TRUE(euicc_path.has_value());

    HermesEuiccClient::Properties* euicc_properties =
        HermesEuiccClient::Get()->GetProperties(*euicc_path);
    ASSERT_TRUE(euicc_properties);
    EXPECT_EQ(physical_slot, euicc_properties->physical_slot().value());
  }

  void CheckHistogramState(const ExpectedHistogramState& state) {
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanViaPolicyProfileCount,
        /*expected_count=*/state.smds_scan_profile_total_count);
    EXPECT_EQ(
        static_cast<int64_t>(state.smds_scan_profile_sum),
        histogram_tester_.GetTotalSum(
            CellularNetworkMetricsLogger::kSmdsScanViaPolicyProfileCount));
    histogram_tester_.ExpectBucketCount(
        CellularNetworkMetricsLogger::kESimPolicyInstallNoAvailableProfiles,
        CellularNetworkMetricsLogger::ESimPolicyInstallMethod::kViaSmdp,
        /*expected_count=*/state.no_available_profiles_via_smdp_count);
    histogram_tester_.ExpectBucketCount(
        CellularNetworkMetricsLogger::kESimPolicyInstallNoAvailableProfiles,
        CellularNetworkMetricsLogger::ESimPolicyInstallMethod::kViaSmds,
        /*expected_count=*/state.no_available_profiles_via_smds_count);
    histogram_tester_.ExpectBucketCount(
        CellularNetworkMetricsLogger::kESimPolicyInstallMethod,
        CellularNetworkMetricsLogger::ESimPolicyInstallMethod::kViaSmdp,
        /*expected_count=*/state.install_method_via_smdp_count);
    histogram_tester_.ExpectBucketCount(
        CellularNetworkMetricsLogger::kESimPolicyInstallMethod,
        CellularNetworkMetricsLogger::ESimPolicyInstallMethod::kViaSmds,
        /*expected_count=*/state.install_method_via_smds_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanAndroidDurationSuccess,
        /*expected_count=*/state.scan_duration_android_success_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanAndroidDurationFailure,
        /*expected_count=*/state.scan_duration_android_failure_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanOtherDurationSuccess,
        /*expected_count=*/state.scan_duration_other_success_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanOtherDurationFailure,
        /*expected_count=*/state.scan_duration_other_failure_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanGsmaDurationSuccess,
        /*expected_count=*/state.scan_duration_gsma_success_count);
    histogram_tester_.ExpectTotalCount(
        CellularNetworkMetricsLogger::kSmdsScanGsmaDurationFailure,
        /*expected_count=*/state.scan_duration_gsma_failure_count);
    state.smds_scan_state.Check(&histogram_tester_);
  }

  // This functionality was explicitly separated from InstallProfile() since
  // multiple tests involve attempting, and failing, to install an eSIM profile.
  // By separating the installation and auto-connect logic we can simply call
  // this method specifically when we expect the installation to succeed.
  void CompleteShillServiceAutoConnect(const base::Value::Dict& onc_config) {
    const std::string* shill_guid = onc_config.FindString(shill::kGuidProperty);
    ASSERT_TRUE(shill_guid);

    const std::string shill_service_path =
        ShillServiceClient::Get()->GetTestInterface()->FindServiceMatchingGUID(
            *shill_guid);
    EXPECT_FALSE(shill_service_path.empty());

    ShillServiceClient::Get()->GetTestInterface()->SetServiceProperty(
        shill_service_path, shill::kStateProperty,
        base::Value(shill::kStateOnline));
    base::RunLoop().RunUntilIdle();
  }

  void FastForwardBy(base::TimeDelta delay) {
    task_environment_.FastForwardBy(delay);
  }

  void FastForwardRefreshDelay() {
    // TODO(crbug.com/1216693): Update when a more robust way of waiting for
    // eSIM profile objects to be loaded is available.
    FastForwardBy(base::Seconds(1));
  }

  // Between the completion of an SM-DS scan and the discovered profile paths
  // actually being provided to CellularPolicyHandler there can be a delay. This
  // delay allows Hermes to update the properties of the discovered profiles
  // before we attempt to read them, e.g. the activation code.
  void FastForwardProfileWaiterDelay() { FastForwardBy(base::Seconds(30)); }

  CellularPolicyHandler* cellular_policy_handler() {
    return cellular_policy_handler_;
  }

  NetworkHandlerTestHelper* network_handler_test_helper() {
    return network_handler_test_helper_.get();
  }

 private:
  void CheckHistogram(const char* histogram,
                      size_t success_count,
                      size_t inhibit_failed_count,
                      size_t hermes_install_failed_count) {
    using InstallESimProfileResult =
        CellularESimInstaller::InstallESimProfileResult;
    histogram_tester_.ExpectBucketCount(histogram,
                                        InstallESimProfileResult::kSuccess,
                                        /*expected_count=*/success_count);
    histogram_tester_.ExpectBucketCount(
        histogram, InstallESimProfileResult::kInhibitFailed,
        /*expected_count=*/inhibit_failed_count);
    histogram_tester_.ExpectBucketCount(
        histogram, InstallESimProfileResult::kHermesInstallFailed,
        /*expected_count=*/hermes_install_failed_count);
  }

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  base::HistogramTester histogram_tester_;
  raw_ptr<CellularPolicyHandler, DanglingUntriaged> cellular_policy_handler_;
  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
  TestingPrefServiceSimple profile_prefs_;
  TestingPrefServiceSimple device_prefs_;
};

TEST_F(CellularPolicyHandlerTest, InstallSuccess_SMDP) {
  SetupGolden();

  // We sanity check that the current EUICC has the expected slot in these core
  // installation success tests.
  CheckCurrentEuiccSlot(0);

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());
  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  CellularInhibitorObserver cellular_inhibitor_observer;
  InstallProfile(*onc_config);

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_EQ(InhibitReason::kRefreshingProfileList,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kRequestingAvailableProfiles,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kInstallingProfile,
            cellular_inhibitor_observer.PopInhibitReason());

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_SMDS) {
  SetupGolden();

  // We sanity check that the current EUICC has the expected slot in these core
  // installation success tests.
  CheckCurrentEuiccSlot(0);

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  // A different value is used to more accurately simulate what will happen when
  // installing a profile via SM-DS; the activation code used for the SM-DS scan
  // are different than the activation codes of the available profiles.
  const std::string different_activation_code_value =
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode();

  HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
      dbus::ObjectPath(kTestProfilePath0), dbus::ObjectPath(kTestEuiccPath0),
      kTestProfileIccid0, kTestProfileName0, kTestProfileNickname0,
      kTestProfileServiceProvider0, different_activation_code_value,
      kTestProfileServicePath0, hermes::profile::State::kPending,
      hermes::profile::ProfileClass::kOperational,
      HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
          kAddProfileWithService);
  base::RunLoop().RunUntilIdle();

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({dbus::ObjectPath(kTestProfilePath0)});

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDS,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  CellularInhibitorObserver cellular_inhibitor_observer;
  InstallProfile(*onc_config);

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_EQ(InhibitReason::kRefreshingProfileList,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kRequestingAvailableProfiles,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kInstallingProfile,
            cellular_inhibitor_observer.PopInhibitReason());

  EXPECT_TRUE(IsProfileInstalled(*onc_config, different_activation_code_value,
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(different_activation_code_value));
  expected_state.success_initial_count++;
  expected_state.smds_scan_profile_total_count++;
  expected_state.smds_scan_profile_sum++;
  expected_state.install_method_via_smds_count++;
  expected_state.scan_duration_other_success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_filtered
      .success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_included
      .success_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_DespiteHermesErrors) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDS,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  // Queue a success result for the call to refresh the profile list.
  HermesEuiccClient::Get()->GetTestInterface()->QueueHermesErrorStatus(
      HermesResponseStatus::kSuccess);

  // Queue a failure result for the SM-DS scan itself.
  HermesEuiccClient::Get()->GetTestInterface()->QueueHermesErrorStatus(
      HermesResponseStatus::kErrorUnknown);
  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.no_available_profiles_via_smds_count++;
  expected_state.smds_scan_profile_total_count++;
  expected_state.scan_duration_other_failure_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_filtered
      .hermes_failed_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_included
      .hermes_failed_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstalledButFailedToEnable) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  // Set the result of the next attempt to enable a carrier profile to match
  // what would be returned when a profile was successfully installed, but
  // failed to become enabled.
  HermesProfileClient::Get()
      ->GetTestInterface()
      ->SetNextEnableCarrierProfileResult(
          HermesResponseStatus::kErrorWrongState);

  CellularInhibitorObserver cellular_inhibitor_observer;
  InstallProfile(*onc_config);

  EXPECT_EQ(InhibitReason::kRefreshingProfileList,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kRequestingAvailableProfiles,
            cellular_inhibitor_observer.PopInhibitReason());
  EXPECT_EQ(InhibitReason::kInstallingProfile,
            cellular_inhibitor_observer.PopInhibitReason());

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_SMDSMultipleProfiles) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  // A different value is used to more accurately simulate what will happen when
  // installing a profile via SM-DS; the activation code used for the SM-DS scan
  // are different than the activation codes of the available profiles.
  const std::string different_activation_code_value =
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode();

  // Add a profile that is already installed and active.
  const dbus::ObjectPath profile_path0 =
      HermesEuiccClient::Get()->GetTestInterface()->AddFakeCarrierProfile(
          dbus::ObjectPath(kTestEuiccPath0), hermes::profile::State::kActive,
          HermesEuiccClient::Get()
              ->GetTestInterface()
              ->GenerateFakeActivationCode(),
          HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
              kAddProfileWithService);

  // Add a profile that is already installed and inactive.
  const dbus::ObjectPath profile_path1 =
      HermesEuiccClient::Get()->GetTestInterface()->AddFakeCarrierProfile(
          dbus::ObjectPath(kTestEuiccPath0), hermes::profile::State::kInactive,
          HermesEuiccClient::Get()
              ->GetTestInterface()
              ->GenerateFakeActivationCode(),
          HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
              kAddProfileWithService);

  // Add a profile that is pending but not expected to be installed since it
  // does not have an activation code.
  const dbus::ObjectPath profile_path2 =
      HermesEuiccClient::Get()->GetTestInterface()->AddFakeCarrierProfile(
          dbus::ObjectPath(kTestEuiccPath0), hermes::profile::State::kActive,
          /*activation_code=*/"",
          HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
              kAddProfileWithService);

  // Add the pending profile that we expect will be installed. This should be
  // the third profile returned, and should be chosen because it is the first
  // profile that is pending and has a valid activation code.
  HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
      dbus::ObjectPath(kTestProfilePath0), dbus::ObjectPath(kTestEuiccPath0),
      kTestProfileIccid0, kTestProfileName0, kTestProfileNickname0,
      kTestProfileServiceProvider0, different_activation_code_value,
      kTestProfileServicePath0, hermes::profile::State::kPending,
      hermes::profile::ProfileClass::kOperational,
      HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
          kAddProfileWithService);

  // Add a profile that is pending but not expected to be installed since only
  // the first profile returned that is pending and has a valid activation code
  // should be installed.
  const dbus::ObjectPath profile_path3 =
      HermesEuiccClient::Get()->GetTestInterface()->AddFakeCarrierProfile(
          dbus::ObjectPath(kTestEuiccPath0), hermes::profile::State::kActive,
          HermesEuiccClient::Get()
              ->GetTestInterface()
              ->GenerateFakeActivationCode(),
          HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
              kAddProfileWithService);
  base::RunLoop().RunUntilIdle();

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({
          profile_path0,
          profile_path1,
          profile_path2,
          dbus::ObjectPath(kTestProfilePath0),
          profile_path3,
      });

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDS,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  FastForwardProfileWaiterDelay();

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, different_activation_code_value,
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(different_activation_code_value));
  expected_state.success_initial_count++;
  expected_state.smds_scan_profile_total_count++;
  expected_state.smds_scan_profile_sum = 5;
  expected_state.install_method_via_smds_count++;
  expected_state.scan_duration_other_success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_filtered
      .success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_included
      .success_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_RequireCellularDevice) {
  AddEuiccs();
  AddWiFi();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  AddCellularDevice();

  FastForwardRefreshDelay();

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_RequireEuicc) {
  AddCellularDevice();
  AddWiFi();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  AddEuiccs();

  FastForwardRefreshDelay();

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_RequireNonCellularConnection) {
  AddCellularDevice();
  AddEuiccs();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  // When there is no non-cellular connectivity the installation attempt is
  // considered failed, and will be retried after a delay.
  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  AddWiFi();

  // The delay for the first failure is 10 minutes. Fast forward to just before
  // the next installation attempt should be.
  FastForwardBy(base::Minutes(9));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  FastForwardBy(base::Minutes(1));

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_retry_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_ExistingIccid) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());
  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code, kTestProfileIccid0));
  ASSERT_TRUE(onc_config.has_value());

  // Add a profile the same ICCID as |onc_config|.
  HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
      dbus::ObjectPath(kTestProfilePath0), dbus::ObjectPath(kTestEuiccPath0),
      kTestProfileIccid0, kTestProfileName0, kTestProfileNickname0,
      kTestProfileServiceProvider0, activation_code.value(),
      kTestProfileServicePath0, hermes::profile::State::kActive,
      hermes::profile::ProfileClass::kOperational,
      HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
          kAddProfileWithService);
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/false));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));

  const base::Value::Dict* properties =
      network_handler_test_helper()->service_test()->GetServiceProperties(
          kTestProfileServicePath0);
  ASSERT_TRUE(properties);

  const std::string* iccid = properties->FindString(shill::kIccidProperty);
  EXPECT_TRUE(iccid && *iccid == kTestProfileIccid0);

  InstallProfile(*onc_config);

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(HasESimMetadata(activation_code.value()));

  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_WaitForProfileProperties) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const dbus::ObjectPath profile_path(kTestProfilePath0);

  // Add a profile that is missing the required properties.
  HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
      profile_path, dbus::ObjectPath(kTestEuiccPath0), kTestProfileIccid0,
      /*name=*/"", kTestProfileNickname0, kTestProfileServiceProvider0,
      /*activation_code=*/"", kTestProfileServicePath0,
      /*state=*/hermes::profile::State::kInactive,
      hermes::profile::ProfileClass::kOperational,
      HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
          kAddProfileWithService);
  base::RunLoop().RunUntilIdle();

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({profile_path});

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());
  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  HermesProfileClient::Properties* profile_properties =
      HermesProfileClient::Get()->GetProperties(profile_path);
  ASSERT_TRUE(profile_properties);

  profile_properties->name().ReplaceValue(/*value=*/kTestProfileName0);
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  profile_properties->activation_code().ReplaceValue(
      /*value=*/activation_code.value());
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);

  profile_properties->state().ReplaceValue(
      /*value=*/hermes::profile::State::kPending);
  base::RunLoop().RunUntilIdle();

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallFailure_NoActivationCodeProvided) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          base::StringPrintf(kCellularPolicyPattern, base::RandUint64(), "{}"));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallFailure_ProfileMissingActivationCode) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  // Configure a profile with no activation code.
  HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
      dbus::ObjectPath(kTestProfilePath0), dbus::ObjectPath(kTestEuiccPath0),
      kTestProfileIccid0, kTestProfileName0, kTestProfileNickname0,
      kTestProfileServiceProvider0, /*activation_code=*/"",
      kTestProfileServicePath0, hermes::profile::State::kPending,
      hermes::profile::ProfileClass::kOperational,
      HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
          kAddProfileWithService);
  base::RunLoop().RunUntilIdle();

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({dbus::ObjectPath(kTestProfilePath0)});

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDS,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.smds_scan_profile_total_count++;
  expected_state.smds_scan_profile_sum++;
  expected_state.scan_duration_other_success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_filtered
      .success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_included
      .success_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallFailure_InternalErrorRetry) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorUnknown);

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);

  // Failures due to Hermes are considered transient and the installation will
  // be retried after a delay. Fast forward to just before we expect the retry.
  FastForwardBy(base::Minutes(9));

  CheckHistogramState(expected_state);

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorUnknown);

  FastForwardBy(base::Minutes(1));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_retry_count++;
  CheckHistogramState(expected_state);

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorUnknown);

  // We don't know how much time has passed since the first retry, so instead of
  // checking before and after when we expect the retry to happen we simply skip
  // forward to when we know the next retry should happen.
  FastForwardBy(base::Minutes(20));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_retry_count++;
  CheckHistogramState(expected_state);

  // Please see the comment above for more context.
  FastForwardBy(base::Minutes(40));

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_retry_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallFailure_OtherErrorRetry) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorSendHttpsFailure);

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);

  // Failures that are not due to Hermes or user behavior are not considered
  // transient and the installation will only be retried after an entire day.
  FastForwardBy(base::Hours(23));

  CheckHistogramState(expected_state);

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorSendHttpsFailure);

  FastForwardBy(base::Hours(1));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_retry_count++;
  CheckHistogramState(expected_state);

  for (int i = 0; i < 2; ++i) {
    HermesEuiccClient::Get()
        ->GetTestInterface()
        ->SetNextInstallProfileFromActivationCodeResult(
            HermesResponseStatus::kErrorSendHttpsFailure);

    // We don't know how much time has passed since the first retry, so instead
    // of checking before and after when we expect the retry to happen we simply
    // skip forward to when we know the next retry should happen.
    FastForwardBy(base::Days(1));

    EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                    /*check_for_service=*/true));
    EXPECT_FALSE(HasESimMetadata(activation_code.value()));
    expected_state.hermes_install_failed_retry_count++;
    CheckHistogramState(expected_state);
  }

  // Failures that are not due to Hermes or user behavior have limit to how many
  // times we will retry the installation. Fast forward an entire week to ensure
  // that we don't continue attempting to install the profile.
  FastForwardBy(base::Days(7));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallFailure_UserError) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextInstallProfileFromActivationCodeResult(
          HermesResponseStatus::kErrorInvalidActivationCode);

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  expected_state.hermes_install_failed_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);

  // Failures that are due to user behavior are not considered transient and the
  // installation will not be retried.
  FastForwardBy(base::Days(7));

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  EXPECT_FALSE(HasESimMetadata(activation_code.value()));
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, InstallSuccess_SecondEuicc) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(ash::features::kCellularUseSecondEuicc);
  SetupGolden();

  CheckCurrentEuiccSlot(1);

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());

  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  InstallProfile(*onc_config);

  CompleteShillServiceAutoConnect(*onc_config);

  EXPECT_TRUE(IsProfileInstalled(*onc_config, activation_code.value(),
                                 /*check_for_service=*/true));
  EXPECT_TRUE(HasESimMetadata(activation_code.value()));
  expected_state.success_initial_count++;
  expected_state.install_method_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, NoAvailableProfiles_SMDP) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDP,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());
  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({});

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  expected_state.no_available_profiles_via_smdp_count++;
  CheckHistogramState(expected_state);
}

TEST_F(CellularPolicyHandlerTest, NoAvailableProfiles_SMDS) {
  SetupGolden();

  ExpectedHistogramState expected_state;
  CheckHistogramState(expected_state);

  const policy_util::SmdxActivationCode activation_code(
      policy_util::SmdxActivationCode::Type::SMDS,
      HermesEuiccClient::Get()
          ->GetTestInterface()
          ->GenerateFakeActivationCode());
  std::optional<base::Value::Dict> onc_config =
      chromeos::onc::ReadDictionaryFromJson(
          GenerateCellularPolicy(activation_code));
  ASSERT_TRUE(onc_config.has_value());

  HermesEuiccClient::Get()
      ->GetTestInterface()
      ->SetNextRefreshSmdxProfilesResult({});

  InstallProfile(*onc_config);

  EXPECT_FALSE(IsProfileInstalled(*onc_config, activation_code.value(),
                                  /*check_for_service=*/true));
  expected_state.no_available_profiles_via_smds_count++;
  expected_state.scan_duration_other_success_count++;
  expected_state.smds_scan_profile_total_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_filtered
      .success_count++;
  expected_state.smds_scan_state.smds_scan_other_user_errors_included
      .success_count++;
  CheckHistogramState(expected_state);
}

}  // namespace ash