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

// Copyright 2019 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_metrics_logger.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/time/tick_clock.h"
#include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
#include "chromeos/ash/components/feature_usage/feature_usage_metrics.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "chromeos/ash/components/network/cellular_esim_profile.h"
#include "chromeos/ash/components/network/cellular_esim_profile_handler.h"
#include "chromeos/ash/components/network/cellular_utils.h"
#include "chromeos/ash/components/network/network_connection_handler.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

const char kESimUMAFeatureName[] = "ESim";
const char kEnterpriseESimUMAFeatureName[] = "EnterpriseESim";

// Checks whether the current logged in user type is an owner or regular.
bool IsLoggedInUserRegular() {
  if (!LoginState::IsInitialized())
    return false;

  LoginState::LoggedInUserType user_type =
      LoginState::Get()->GetLoggedInUserType();
  return user_type == LoginState::LoggedInUserType::LOGGED_IN_USER_REGULAR;
}

SimType GetSimType(const NetworkState* network) {
  return network->eid().empty() ? SimType::kPSim : SimType::kESim;
}

}  // namespace

// static
const char
    CellularMetricsLogger::kESimUserInitiatedConnectionResultHistogram[] =
        "Network.Cellular.ESim.ConnectionResult.UserInitiated";

// static
const char
    CellularMetricsLogger::kPSimUserInitiatedConnectionResultHistogram[] =
        "Network.Cellular.PSim.ConnectionResult.UserInitiated";

// static
const char CellularMetricsLogger::kESimAllConnectionResultHistogram[] =
    "Network.Cellular.ESim.ConnectionResult.All";

// static
const char CellularMetricsLogger::kESimPolicyAllConnectionResultHistogram[] =
    "Network.Cellular.ESim.Policy.ConnectionResult.All";

// static
const char CellularMetricsLogger::kPSimAllConnectionResultHistogram[] =
    "Network.Cellular.PSim.ConnectionResult.All";

// static
const char CellularMetricsLogger::kSimPinRequireLockSuccessHistogram[] =
    "Network.Cellular.Pin.RequireLockSuccess";

// static
const char CellularMetricsLogger::kSimPinRemoveLockSuccessHistogram[] =
    "Network.Cellular.Pin.RemoveLockSuccess";

// static
const char CellularMetricsLogger::kUnmanagedSimPinUnlockSuccessHistogram[] =
    "Network.Cellular.Pin.Unmanaged.UnlockSuccess";

// static
const char CellularMetricsLogger::kManagedSimPinUnlockSuccessHistogram[] =
    "Network.Cellular.Pin.Managed.UnlockSuccess";

// static
const char CellularMetricsLogger::kUnmanagedSimPinUnblockSuccessHistogram[] =
    "Network.Cellular.Pin.Unmanaged.UnblockSuccess";

// static
const char CellularMetricsLogger::kManagedSimPinUnblockSuccessHistogram[] =
    "Network.Cellular.Pin.Managed.UnblockSuccess";

// static
const char CellularMetricsLogger::kChangePinSuccessSimPinLockPolicyHistogram[] =
    "Network.Cellular.ChangePin.SimPINLockPolicy";

// static
const char
    CellularMetricsLogger::kRequirePinSuccessSimPinLockPolicyHistogram[] =
        "Network.Cellular.RequirePin.SimPINLockPolicy";

// static
const char CellularMetricsLogger::kSimPinChangeSuccessHistogram[] =
    "Network.Cellular.Pin.ChangeSuccess";

// static
const char CellularMetricsLogger::kSimLockNotificationEventHistogram[] =
    "Network.Ash.Cellular.SimLock.Policy.Notification.Event";

// static
const char CellularMetricsLogger::kUnrestrictedSimPinUnlockSuccessHistogram[] =
    "Network.Cellular.Pin.Unrestricted.UnlockSuccess";

// static
const char CellularMetricsLogger::kRestrictedSimPinUnlockSuccessHistogram[] =
    "Network.Cellular.Pin.Restricted.UnlockSuccess";

// static
const char CellularMetricsLogger::kUnrestrictedSimPinUnblockSuccessHistogram[] =
    "Network.Cellular.Pin.Unrestricted.UnblockSuccess";

// static
const char CellularMetricsLogger::kRestrictedSimPinUnblockSuccessHistogram[] =
    "Network.Cellular.Pin.Restricted.UnblockSuccess";

// static
const char CellularMetricsLogger::kSimLockNotificationLockType[] =
    "Network.Ash.Cellular.SimLock.Policy.Notification.LockType";

// static
const char CellularMetricsLogger::kUnrestrictedActiveNetworkSIMLockStatus[] =
    "Network.Ash.Cellular.SimLock.Policy.Unrestricted.ActiveSIMLockStatus";

// static
const char CellularMetricsLogger::kRestrictedActiveNetworkSIMLockStatus[] =
    "Network.Ash.Cellular.SimLock.Policy.Restricted.ActiveSIMLockStatus";

// static
const base::TimeDelta CellularMetricsLogger::kInitializationTimeout =
    base::Seconds(15);

// static
const base::TimeDelta CellularMetricsLogger::kDisconnectRequestTimeout =
    base::Seconds(5);

// static
CellularMetricsLogger::SimPinOperationResult
CellularMetricsLogger::GetSimPinOperationResultForShillError(
    const std::string& shill_error_name) {
  if (shill_error_name == shill::kErrorResultFailure ||
      shill_error_name == shill::kErrorResultInvalidArguments) {
    return SimPinOperationResult::kErrorFailure;
  }
  if (shill_error_name == shill::kErrorResultNotSupported)
    return SimPinOperationResult::kErrorNotSupported;
  if (shill_error_name == shill::kErrorResultIncorrectPin)
    return SimPinOperationResult::kErrorIncorrectPin;
  if (shill_error_name == shill::kErrorResultPinBlocked)
    return SimPinOperationResult::kErrorPinBlocked;
  if (shill_error_name == shill::kErrorResultPinRequired)
    return SimPinOperationResult::kErrorPinRequired;
  if (shill_error_name == shill::kErrorResultNotFound)
    return SimPinOperationResult::kErrorDeviceMissing;
  if (shill_error_name == shill::kErrorResultWrongState)
    return SimPinOperationResult::kErrorWrongState;
  return SimPinOperationResult::kErrorUnknown;
}

// static
void CellularMetricsLogger::RecordSimLockNotificationEvent(
    const SimLockNotificationEvent notification_event) {
  base::UmaHistogramEnumeration(kSimLockNotificationEventHistogram,
                                notification_event);
}

// static
void CellularMetricsLogger::RecordSimLockNotificationLockType(
    const std::string& sim_lock_type) {
  if (sim_lock_type == shill::kSIMLockPin) {
    base::UmaHistogramEnumeration(kSimLockNotificationLockType,
                                  SimPinLockType::kPinLocked);
  } else if (sim_lock_type == shill::kSIMLockPuk) {
    base::UmaHistogramEnumeration(kSimLockNotificationLockType,
                                  SimPinLockType::kPukLocked);
  } else if (sim_lock_type == shill::kSIMLockNetworkPin) {
    base::UmaHistogramEnumeration(kSimLockNotificationLockType,
                                  SimPinLockType::kCarrierLocked);
  } else {
    NOTREACHED_IN_MIGRATION();
  }
}

// static
void CellularMetricsLogger::RecordSimPinOperationResult(
    const SimPinOperation& pin_operation,
    const bool allow_cellular_sim_lock,
    const std::optional<std::string>& shill_error_name) {
  SimPinOperationResult result =
      shill_error_name.has_value()
          ? GetSimPinOperationResultForShillError(*shill_error_name)
          : SimPinOperationResult::kSuccess;
  bool is_enterprise_managed = NetworkHandler::Get()->is_enterprise_managed();

  switch (pin_operation) {
    case SimPinOperation::kRequireLock:
      base::UmaHistogramBoolean(kRequirePinSuccessSimPinLockPolicyHistogram,
                                allow_cellular_sim_lock);
      base::UmaHistogramEnumeration(kSimPinRequireLockSuccessHistogram, result);
      return;
    case SimPinOperation::kRemoveLock:
      base::UmaHistogramEnumeration(kSimPinRemoveLockSuccessHistogram, result);
      return;
    case SimPinOperation::kUnlock:
      if (is_enterprise_managed) {
        base::UmaHistogramEnumeration(kManagedSimPinUnlockSuccessHistogram,
                                      result);
        base::UmaHistogramEnumeration(
            allow_cellular_sim_lock ? kUnrestrictedSimPinUnlockSuccessHistogram
                                    : kRestrictedSimPinUnlockSuccessHistogram,
            result);
      } else {
        base::UmaHistogramEnumeration(kUnmanagedSimPinUnlockSuccessHistogram,
                                      result);
      }
      return;
    case SimPinOperation::kUnblock:
      if (is_enterprise_managed) {
        base::UmaHistogramEnumeration(kManagedSimPinUnblockSuccessHistogram,
                                      result);
        base::UmaHistogramEnumeration(
            allow_cellular_sim_lock ? kUnrestrictedSimPinUnblockSuccessHistogram
                                    : kRestrictedSimPinUnblockSuccessHistogram,
            result);
      } else {
        base::UmaHistogramEnumeration(kUnmanagedSimPinUnblockSuccessHistogram,
                                      result);
      }
      return;
    case SimPinOperation::kChange:
      base::UmaHistogramBoolean(kChangePinSuccessSimPinLockPolicyHistogram,
                                allow_cellular_sim_lock);
      base::UmaHistogramEnumeration(kSimPinChangeSuccessHistogram, result);
      return;
  }
}

// static
void CellularMetricsLogger::LogCellularUserInitiatedConnectionSuccessHistogram(
    CellularMetricsLogger::ConnectResult start_connect_result,
    SimType sim_type) {
  if (sim_type == SimType::kPSim) {
    base::UmaHistogramEnumeration(kPSimUserInitiatedConnectionResultHistogram,
                                  start_connect_result);
  } else {
    base::UmaHistogramEnumeration(kESimUserInitiatedConnectionResultHistogram,
                                  start_connect_result);
  }
}

// static
CellularMetricsLogger::ConnectResult
CellularMetricsLogger::NetworkConnectionErrorToConnectResult(
    const std::string& error_name) {
  if (error_name == NetworkConnectionHandler::kErrorNotFound)
    return CellularMetricsLogger::ConnectResult::kInvalidGuid;

  if (error_name == NetworkConnectionHandler::kErrorConnected ||
      error_name == NetworkConnectionHandler::kErrorConnecting ||
      error_name == NetworkConnectionHandler::kErrorNotConnected ||
      error_name ==
          NetworkConnectionHandler::kErrorTetherAttemptWithNoDelegate) {
    return CellularMetricsLogger::ConnectResult::kInvalidState;
  }

  if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
    return CellularMetricsLogger::ConnectResult::kCanceled;

  if (error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
      error_name == NetworkConnectionHandler::kErrorBadPassphrase ||
      error_name == NetworkConnectionHandler::kErrorCertificateRequired ||
      error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
      error_name == NetworkConnectionHandler::kErrorAuthenticationRequired ||
      error_name == NetworkConnectionHandler::kErrorCertLoadTimeout ||
      error_name == NetworkConnectionHandler::kErrorConfigureFailed ||
      error_name == NetworkConnectionHandler::kErrorHexSsidRequired) {
    return CellularMetricsLogger::ConnectResult::kNotConfigured;
  }

  if (error_name == NetworkConnectionHandler::kErrorBlockedByPolicy)
    return CellularMetricsLogger::ConnectResult::kBlocked;

  if (error_name == NetworkConnectionHandler::kErrorCellularInhibitFailure)
    return CellularMetricsLogger::ConnectResult::kCellularInhibitFailure;

  if (error_name == NetworkConnectionHandler::kErrorESimProfileIssue)
    return CellularMetricsLogger::ConnectResult::kESimProfileIssue;

  if (error_name == NetworkConnectionHandler::kErrorCellularOutOfCredits)
    return CellularMetricsLogger::ConnectResult::kCellularOutOfCredits;

  if (error_name == NetworkConnectionHandler::kErrorSimPinPukLocked) {
    return CellularMetricsLogger::ConnectResult::kSimPinPukLocked;
  }

  if (error_name == NetworkConnectionHandler::kErrorSimCarrierLocked) {
    return CellularMetricsLogger::ConnectResult::kSimCarrierLocked;
  }

  if (error_name == NetworkConnectionHandler::kErrorConnectFailed)
    return CellularMetricsLogger::ConnectResult::kConnectFailed;

  if (error_name == NetworkConnectionHandler::kErrorActivateFailed)
    return CellularMetricsLogger::ConnectResult::kActivateFailed;

  if (error_name ==
      NetworkConnectionHandler::kErrorEnabledOrDisabledWhenNotAvailable) {
    return CellularMetricsLogger::ConnectResult::
        kEnabledOrDisabledWhenNotAvailable;
  }

  if (error_name == NetworkConnectionHandler::kErrorCellularDeviceBusy)
    return CellularMetricsLogger::ConnectResult::kErrorCellularDeviceBusy;

  if (error_name == NetworkConnectionHandler::kErrorConnectTimeout)
    return CellularMetricsLogger::ConnectResult::kErrorConnectTimeout;

  if (error_name == NetworkConnectionHandler::kConnectableCellularTimeout)
    return CellularMetricsLogger::ConnectResult::kConnectableCellularTimeout;

  return CellularMetricsLogger::ConnectResult::kUnknown;
}

// static
CellularMetricsLogger::ShillConnectResult
CellularMetricsLogger::ShillErrorToConnectResult(
    const std::string& error_name) {
  if (error_name == shill::kErrorBadPassphrase)
    return CellularMetricsLogger::ShillConnectResult::kBadPassphrase;
  else if (error_name == shill::kErrorBadWEPKey)
    return CellularMetricsLogger::ShillConnectResult::kBadWepKey;
  else if (error_name == shill::kErrorConnectFailed)
    return CellularMetricsLogger::ShillConnectResult::kFailedToConnect;
  else if (error_name == shill::kErrorDhcpFailed)
    return CellularMetricsLogger::ShillConnectResult::kDhcpFailure;
  else if (error_name == shill::kErrorDNSLookupFailed)
    return CellularMetricsLogger::ShillConnectResult::kDnsLookupFailure;
  else if (error_name == shill::kErrorEapAuthenticationFailed)
    return CellularMetricsLogger::ShillConnectResult::kEapAuthentication;
  else if (error_name == shill::kErrorEapLocalTlsFailed)
    return CellularMetricsLogger::ShillConnectResult::kEapLocalTls;
  else if (error_name == shill::kErrorEapRemoteTlsFailed)
    return CellularMetricsLogger::ShillConnectResult::kEapRemoteTls;
  else if (error_name == shill::kErrorOutOfRange)
    return CellularMetricsLogger::ShillConnectResult::kOutOfRange;
  else if (error_name == shill::kErrorPinMissing)
    return CellularMetricsLogger::ShillConnectResult::kPinMissing;
  else if (error_name == shill::kErrorNoFailure)
    return CellularMetricsLogger::ShillConnectResult::kNoFailure;
  else if (error_name == shill::kErrorNotAssociated)
    return CellularMetricsLogger::ShillConnectResult::kNotAssociated;
  else if (error_name == shill::kErrorNotAuthenticated)
    return CellularMetricsLogger::ShillConnectResult::kNotAuthenticated;
  else if (error_name == shill::kErrorSimLocked)
    return CellularMetricsLogger::ShillConnectResult::kErrorSimPinPukLocked;
  else if (error_name == shill::kErrorSimCarrierLocked) {
    return CellularMetricsLogger::ShillConnectResult::kErrorSimCarrierLocked;
  } else if (error_name == shill::kErrorNotRegistered) {
    return CellularMetricsLogger::ShillConnectResult::kErrorNotRegistered;
  }
  return CellularMetricsLogger::ShillConnectResult::kUnknown;
}

class ESimFeatureUsageMetricsBase {
 public:
  ESimFeatureUsageMetricsBase(NetworkStateHandler* network_state_handler)
      : network_state_handler_(network_state_handler) {}

  virtual ~ESimFeatureUsageMetricsBase() = default;

  // Returns whether there are any known EUICCs.
  bool HasAvailableEuiccs() const {
    return HermesManagerClient::Get() &&
           HermesManagerClient::Get()->GetAvailableEuiccs().size() != 0;
  }

  // Returns whether there is a network state, i.e., a Shill service, for an
  // eSIM profile. When |expect_managed| is |true| this function will require
  // that the network is managed.
  bool HasESimNetwork(bool expect_managed) const {
    NetworkStateHandler::NetworkStateList network_list;
    network_state_handler_->GetNetworkListByType(
        ash::NetworkTypePattern::Cellular(), /*configured_only=*/false,
        /*visible_only=*/true, /*default_limit=*/-1, &network_list);

    for (const NetworkState* network : network_list) {
      // Skip all pSIM networks.
      if (network->eid().empty()) {
        continue;
      }
      if (expect_managed && !network->IsManagedByPolicy()) {
        continue;
      }
      return true;
    }
    return false;
  }

 protected:
  raw_ptr<NetworkStateHandler> network_state_handler_ = nullptr;
};

// Reports daily eSIM Standard Feature Usage Logging metrics. Note that if an
// object of this type is destroyed and created in the same day, metrics
// eligibility and enablement will only be reported once. Registers to local
// state prefs instead of profile prefs as cellular network is available to
// anyone using the device, as opposed to per profile basis.
class ESimFeatureUsageMetrics
    : public feature_usage::FeatureUsageMetrics::Delegate,
      public ESimFeatureUsageMetricsBase {
 public:
  ESimFeatureUsageMetrics(NetworkStateHandler* network_state_handler)
      : ESimFeatureUsageMetricsBase(network_state_handler) {
    feature_usage_metrics_ =
        std::make_unique<feature_usage::FeatureUsageMetrics>(
            kESimUMAFeatureName, this);
  }

  ~ESimFeatureUsageMetrics() override = default;

  // feature_usage::FeatureUsageMetrics::Delegate:
  bool IsEligible() const final {
    return HasAvailableEuiccs() || HasESimNetwork(/*expect_managed=*/false);
  }

  // feature_usage::FeatureUsageMetrics::Delegate:
  bool IsEnabled() const final {
    if (!IsEligible()) {
      return false;
    }

    // Check for eSIM profiles known to Shill.
    if (HasESimNetwork(/*expect_managed=*/false)) {
      return true;
    }

    // Check for eSIM profiles known to Hermes.
    for (const dbus::ObjectPath& euicc_path :
         HermesManagerClient::Get()->GetAvailableEuiccs()) {
      HermesEuiccClient::Properties* euicc_properties =
          HermesEuiccClient::Get()->GetProperties(euicc_path);
      if (!euicc_properties) {
        continue;
      }
      const std::vector<dbus::ObjectPath>& profiles =
          euicc_properties->profiles().value();
      for (const dbus::ObjectPath& profile_path : profiles) {
        HermesProfileClient::Properties* profile_properties =
            HermesProfileClient::Get()->GetProperties(profile_path);
        if (!profile_properties) {
          continue;
        }
        const hermes::profile::State profile_state =
            profile_properties->state().value();
        if (profile_state == hermes::profile::State::kActive ||
            profile_state == hermes::profile::State::kInactive) {
          return true;
        }
      }
    }
    return false;
  }

  // Should be called after an attempt to connect to an eSIM profile.
  void RecordUsage(bool success) const {
    DCHECK(IsEnabled());
    feature_usage_metrics_->RecordUsage(success);
  }

  void StartUsage() { feature_usage_metrics_->StartSuccessfulUsage(); }
  void StopUsage() { feature_usage_metrics_->StopSuccessfulUsage(); }

 private:
  std::unique_ptr<feature_usage::FeatureUsageMetrics> feature_usage_metrics_;
};

// Reports daily enterprise eSIM Standard Feature Usage Logging metrics. Note
// that if an object of this type is destroyed and created in the same day,
// metrics eligibility and enablement will only be reported once. Registers to
// local state prefs instead of profile prefs as cellular network is available
// to anyone using the device, as opposed to per profile basis.
class EnterpriseESimFeatureUsageMetrics
    : public feature_usage::FeatureUsageMetrics::Delegate,
      public ESimFeatureUsageMetricsBase {
 public:
  EnterpriseESimFeatureUsageMetrics(
      NetworkStateHandler* network_state_handler,
      ManagedNetworkConfigurationHandler* managed_network_configuration_handler)
      : ESimFeatureUsageMetricsBase(network_state_handler),
        managed_network_configuration_handler_(
            managed_network_configuration_handler) {
    feature_usage_metrics_ =
        std::make_unique<feature_usage::FeatureUsageMetrics>(
            kEnterpriseESimUMAFeatureName, this);
    DCHECK(InstallAttributes::IsInitialized() &&
           InstallAttributes::Get()->IsEnterpriseManaged());
  }

  ~EnterpriseESimFeatureUsageMetrics() override = default;

  // feature_usage::FeatureUsageMetrics::Delegate:
  bool IsEligible() const final {
    return HasAvailableEuiccs() || HasESimNetwork(/*expect_managed=*/false);
  }

  // feature_usage::FeatureUsageMetrics::Delegate:
  std::optional<bool> IsAccessible() const final {
    if (!IsEligible()) {
      return false;
    }

    // Check if there is already a managed eSIM network known.
    if (HasESimNetwork(/*expect_managed=*/true)) {
      return true;
    }

    const base::Value::Dict* policy =
        managed_network_configuration_handler_->GetGlobalConfigFromPolicy(
            /*userhash=*/std::string());
    if (!policy) {
      return false;
    }
    const base::Value::List* network_configurations =
        policy->FindList(::onc::toplevel_config::kNetworkConfigurations);
    if (!network_configurations) {
      return false;
    }
    for (const auto& network_configuration : *network_configurations) {
      const std::string* type = network_configuration.GetDict().FindString(
          ::onc::network_config::kType);
      if (type && *type == ::onc::network_config::kCellular) {
        return true;
      }
    }
    return false;
  }

  // feature_usage::FeatureUsageMetrics::Delegate:
  bool IsEnabled() const final {
    if (!IsAccessible().value_or(false)) {
      return false;
    }
    return HasESimNetwork(/*expect_managed=*/true);
  }

  // Should be called after an attempt to connect to an eSIM profile.
  void RecordUsage(bool success) const {
    DCHECK(IsEnabled());
    feature_usage_metrics_->RecordUsage(success);
  }

  void StartUsage() { feature_usage_metrics_->StartSuccessfulUsage(); }
  void StopUsage() { feature_usage_metrics_->StopSuccessfulUsage(); }

 private:
  raw_ptr<ManagedNetworkConfigurationHandler>
      managed_network_configuration_handler_ = nullptr;
  std::unique_ptr<feature_usage::FeatureUsageMetrics> feature_usage_metrics_;
};

void CellularMetricsLogger::LogCellularAllConnectionSuccessHistogram(
    CellularMetricsLogger::ShillConnectResult start_connect_result,
    SimType sim_type,
    bool is_managed_by_policy) {
  if (sim_type == SimType::kPSim) {
    base::UmaHistogramEnumeration(kPSimAllConnectionResultHistogram,
                                  start_connect_result);
  } else {
    base::UmaHistogramEnumeration(kESimAllConnectionResultHistogram,
                                  start_connect_result);
    if (is_managed_by_policy) {
      base::UmaHistogramEnumeration(kESimPolicyAllConnectionResultHistogram,
                                    start_connect_result);
    }

    // If there is a failure to connect, log a failed usage attempt to
    // FeatureUsageMetrics.
    if (start_connect_result !=
        CellularMetricsLogger::ShillConnectResult::kSuccess) {
      esim_feature_usage_metrics_->RecordUsage(/*success=*/false);
      if (is_managed_by_policy && enterprise_esim_feature_usage_metrics_) {
        enterprise_esim_feature_usage_metrics_->RecordUsage(/*success=*/false);
      }
    }
  }
}

CellularMetricsLogger::ConnectionInfo::ConnectionInfo(
    const std::string& network_guid,
    bool is_connected,
    bool is_connecting)
    : network_guid(network_guid),
      is_connected(is_connected),
      is_connecting(is_connecting) {}

CellularMetricsLogger::ConnectionInfo::ConnectionInfo(
    const std::string& network_guid)
    : network_guid(network_guid) {}

CellularMetricsLogger::ConnectionInfo::~ConnectionInfo() = default;

CellularMetricsLogger::CellularMetricsLogger() = default;

CellularMetricsLogger::~CellularMetricsLogger() {
  if (network_state_handler_)
    OnShuttingDown();

  if (initialized_) {
    if (LoginState::IsInitialized())
      LoginState::Get()->RemoveObserver(this);

    if (network_connection_handler_)
      network_connection_handler_->RemoveObserver(this);
  }
}

void CellularMetricsLogger::Init(
    NetworkStateHandler* network_state_handler,
    NetworkConnectionHandler* network_connection_handler,
    CellularESimProfileHandler* cellular_esim_profile_handler,
    ManagedNetworkConfigurationHandler* managed_network_configuration_handler) {
  network_state_handler_ = network_state_handler;
  cellular_esim_profile_handler_ = cellular_esim_profile_handler;
  managed_network_configuration_handler_ =
      managed_network_configuration_handler;
  network_state_handler_observer_.Observe(network_state_handler_.get());

  if (network_connection_handler) {
    network_connection_handler_ = network_connection_handler;
    network_connection_handler_->AddObserver(this);
  }

  esim_feature_usage_metrics_ =
      std::make_unique<ESimFeatureUsageMetrics>(network_state_handler_);
  if (InstallAttributes::IsInitialized() &&
      InstallAttributes::Get()->IsEnterpriseManaged()) {
    enterprise_esim_feature_usage_metrics_ =
        std::make_unique<EnterpriseESimFeatureUsageMetrics>(
            network_state_handler_, managed_network_configuration_handler_);
  }

  if (LoginState::IsInitialized())
    LoginState::Get()->AddObserver(this);

  // Devices and networks may already be present before this method is called.
  // Make sure that lists and timers are initialized properly.
  DeviceListChanged();
  NetworkListChanged();
  initialized_ = true;
}

void CellularMetricsLogger::DeviceListChanged() {
  NetworkStateHandler::DeviceStateList device_list;
  network_state_handler_->GetDeviceListByType(NetworkTypePattern::Cellular(),
                                              &device_list);
  bool new_is_cellular_available = !device_list.empty();
  if (is_cellular_available_ == new_is_cellular_available)
    return;

  is_cellular_available_ = new_is_cellular_available;
  // Start a timer to wait for cellular networks to initialize.
  // This makes sure that intermediate not-connected states are
  // not logged before initialization is completed.
  if (is_cellular_available_) {
    initialization_timer_.Start(
        FROM_HERE, kInitializationTimeout, this,
        &CellularMetricsLogger::OnInitializationTimeout);
  }
}

void CellularMetricsLogger::NetworkListChanged() {
  base::flat_map<std::string, std::unique_ptr<ConnectionInfo>>
      old_connection_info_map;
  // Clear |guid_to_connection_info_map| so that only new and existing
  // networks are added back to it.
  old_connection_info_map.swap(guid_to_connection_info_map_);

  NetworkStateHandler::NetworkStateList network_list;
  network_state_handler_->GetVisibleNetworkListByType(
      NetworkTypePattern::Cellular(), &network_list);

  // Check the current cellular networks list and copy existing connection info
  // from old map to new map or create new ones if it does not exist.
  for (const auto* network : network_list) {
    const std::string& guid = network->guid();
    auto old_connection_info_map_iter = old_connection_info_map.find(guid);
    if (old_connection_info_map_iter != old_connection_info_map.end()) {
      guid_to_connection_info_map_.insert_or_assign(
          guid, std::move(old_connection_info_map_iter->second));
      old_connection_info_map.erase(old_connection_info_map_iter);
      continue;
    }

    guid_to_connection_info_map_.insert_or_assign(
        guid,
        std::make_unique<ConnectionInfo>(guid, network->IsConnectedState(),
                                         network->IsConnectingState()));
  }
}

void CellularMetricsLogger::OnInitializationTimeout() {
  CheckForPSimActivationStateMetric();
  CheckForESimProfileStatusMetric();
  CheckForCellularUsageMetrics();
  CheckForCellularServiceCountMetric();
  CheckForApnPolicyMetric();
}

void CellularMetricsLogger::LoggedInStateChanged() {
  if (!IsLoggedInUserRegular()) {
    return;
  }

  // This flag enures that activation state is only logged once when
  // the user logs in.
  is_psim_activation_state_logged_ = false;
  CheckForPSimActivationStateMetric();

  // This flag enures that activation state is only logged once when
  // the user logs in.
  is_esim_profile_status_logged_ = false;
  CheckForESimProfileStatusMetric();

  // This flag ensures that the service count is only logged once when
  // the user logs in.
  is_service_count_logged_ = false;
  CheckForCellularServiceCountMetric();

  // This flag ensures that the APN policy information is only logged
  // when the user logs in.
  is_apn_policy_logged_ = false;
  CheckForApnPolicyMetric();
}

void CellularMetricsLogger::NetworkConnectionStateChanged(
    const NetworkState* network) {
  DCHECK(network_state_handler_);
  CheckForCellularUsageMetrics();

  if (network->type().empty() ||
      !network->Matches(NetworkTypePattern::Cellular())) {
    return;
  }

  CheckForTimeToConnectedMetric(network);
  // Check for connection failures triggered by shill changes, unlike in
  // ConnectFailed() which is triggered by connection attempt failures at
  // chrome layers.
  CheckForShillConnectionFailureMetric(network);
  CheckForConnectionStateMetric(network);
  CheckForSIMStatusMetric(network);
}

void CellularMetricsLogger::CheckForSIMStatusMetric(
    const NetworkState* network) {
  const DeviceState* cellular_device =
      network_state_handler_->GetDeviceState(network->device_path());
  if (!cellular_device || network->IsConnectingState()) {
    return;
  }

  const std::string& sim_lock_type = cellular_device->sim_lock_type();

  if (last_active_network_iccid_ == network->iccid() ||
      (!network->IsConnectedState() && sim_lock_type.empty())) {
    return;
  }

  last_active_network_iccid_ = network->iccid();
  SimPinLockType lock_type;

  if (sim_lock_type == shill::kSIMLockPin) {
    lock_type = SimPinLockType::kPinLocked;
  } else if (sim_lock_type == shill::kSIMLockPuk) {
    lock_type = SimPinLockType::kPukLocked;
  } else if (sim_lock_type == shill::kSIMLockNetworkPin) {
    lock_type = SimPinLockType::kCarrierLocked;
  } else if (sim_lock_type.empty()) {
    lock_type = SimPinLockType::kUnlocked;
  } else {
    NOTREACHED_IN_MIGRATION();
  }

  if (managed_network_configuration_handler_->AllowCellularSimLock()) {
    base::UmaHistogramEnumeration(kUnrestrictedActiveNetworkSIMLockStatus,
                                  lock_type);
  } else {
    base::UmaHistogramEnumeration(kRestrictedActiveNetworkSIMLockStatus,
                                  lock_type);
  }
}

void CellularMetricsLogger::CheckForTimeToConnectedMetric(
    const NetworkState* network) {
  if (network->activation_state() != shill::kActivationStateActivated)
    return;

  // We could be receiving a connection state change for a network different
  // from the one observed when the start time was recorded. Make sure that we
  // only look up time to connected of the corresponding network.
  ConnectionInfo* connection_info =
      GetConnectionInfoForCellularNetwork(network->guid());

  if (network->IsConnectingState()) {
    if (!connection_info->last_connect_start_time.has_value())
      connection_info->last_connect_start_time = base::TimeTicks::Now();

    return;
  }

  if (!connection_info->last_connect_start_time.has_value())
    return;

  if (network->IsConnectedState()) {
    base::TimeDelta time_to_connected =
        base::TimeTicks::Now() - *connection_info->last_connect_start_time;

    if (GetSimType(network) == SimType::kPSim) {
      UMA_HISTOGRAM_MEDIUM_TIMES("Network.Cellular.PSim.TimeToConnected",
                                 time_to_connected);
    } else {
      UMA_HISTOGRAM_MEDIUM_TIMES("Network.Cellular.ESim.TimeToConnected",
                                 time_to_connected);
    }
  }

  // This is hit when the network is no longer in connecting state,
  // successfully connected or otherwise. Reset the connect start_time
  // so that it is not used for further connection state changes.
  connection_info->last_connect_start_time.reset();
}

void CellularMetricsLogger::ConnectSucceeded(const std::string& service_path) {
  const NetworkState* network = GetCellularNetwork(service_path);
  if (!network)
    return;

  LogCellularUserInitiatedConnectionSuccessHistogram(ConnectResult::kSuccess,
                                                     GetSimType(network));
}

void CellularMetricsLogger::ConnectFailed(const std::string& service_path,
                                          const std::string& error_name) {
  const NetworkState* network = GetCellularNetwork(service_path);
  if (!network)
    return;

  LogCellularUserInitiatedConnectionSuccessHistogram(
      NetworkConnectionErrorToConnectResult(error_name), GetSimType(network));
}

void CellularMetricsLogger::DisconnectRequested(
    const std::string& service_path) {
  const NetworkState* network = GetCellularNetwork(service_path);
  if (!network || !network->Matches(NetworkTypePattern::Cellular())) {
    return;
  }

  ConnectionInfo* connection_info =
      GetConnectionInfoForCellularNetwork(network->guid());

  // A disconnect request could fail and result in no cellular connection state
  // change. Save the request time so that only disconnections that do not
  // correspond to a request received within |kDisconnectRequestTimeout| are
  // tracked.
  connection_info->last_disconnect_request_time = base::TimeTicks::Now();
  connection_info->disconnect_requested = true;
}

const NetworkState* CellularMetricsLogger::GetCellularNetwork(
    const std::string& service_path) {
  const NetworkState* network =
      network_state_handler_->GetNetworkState(service_path);
  if (!network || !network->Matches(NetworkTypePattern::Cellular()))
    return nullptr;
  return network;
}

CellularMetricsLogger::PSimActivationState
CellularMetricsLogger::PSimActivationStateToEnum(const std::string& state) {
  if (state == shill::kActivationStateActivated)
    return PSimActivationState::kActivated;
  else if (state == shill::kActivationStateActivating)
    return PSimActivationState::kActivating;
  else if (state == shill::kActivationStateNotActivated)
    return PSimActivationState::kNotActivated;
  else if (state == shill::kActivationStatePartiallyActivated)
    return PSimActivationState::kPartiallyActivated;

  return PSimActivationState::kUnknown;
}

void CellularMetricsLogger::LogCellularDisconnectionsHistogram(
    ConnectionState connection_state,
    SimType sim_type,
    bool is_managed_by_policy) {
  if (sim_type == SimType::kPSim) {
    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.PSim.Disconnections",
                              connection_state);
  } else {
    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.Disconnections",
                              connection_state);
    if (is_managed_by_policy) {
      UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.Policy.Disconnections",
                                connection_state);
    }
  }
}

void CellularMetricsLogger::CheckForShillConnectionFailureMetric(
    const NetworkState* network) {
  ConnectionInfo* connection_info =
      GetConnectionInfoForCellularNetwork(network->guid());

  // If the network connection failed to connect from a connecting state, and no
  // disconnection was requested. Note that |network->connection_state()| being
  // shill::kStateFailure or an empty |network-GetError()| is unreliable after
  // repeated attempts to connect to a network that will fail.
  if (!network->IsConnectingOrConnected() && connection_info->is_connecting &&
      !connection_info->disconnect_requested) {
    LogCellularAllConnectionSuccessHistogram(
        ShillErrorToConnectResult(network->GetError()), GetSimType(network),
        network->IsManagedByPolicy());
  }

  connection_info->is_connecting = network->IsConnectingState();
  connection_info->disconnect_requested = false;
}

void CellularMetricsLogger::CheckForConnectionStateMetric(
    const NetworkState* network) {
  ConnectionInfo* connection_info =
      GetConnectionInfoForCellularNetwork(network->guid());

  bool new_is_connected = network->IsConnectedState();
  if (connection_info->is_connected == new_is_connected)
    return;
  std::optional<bool> old_is_connected = connection_info->is_connected;
  connection_info->is_connected = new_is_connected;

  if (new_is_connected) {
    LogCellularAllConnectionSuccessHistogram(
        CellularMetricsLogger::ShillConnectResult::kSuccess,
        GetSimType(network), network->IsManagedByPolicy());
    LogCellularDisconnectionsHistogram(ConnectionState::kConnected,
                                       GetSimType(network),
                                       network->IsManagedByPolicy());
    connection_info->last_disconnect_request_time.reset();
    return;
  }

  // If the previous connection state is nullopt then this is a new connection
  // info entry and a disconnection did not really occur. Skip logging the
  // metric in this case.
  if (!old_is_connected.has_value())
    return;

  std::optional<base::TimeDelta> time_since_disconnect_requested;
  if (connection_info->last_disconnect_request_time) {
    time_since_disconnect_requested =
        base::TimeTicks::Now() - *connection_info->last_disconnect_request_time;
  }

  // If the disconnect occurred in less than |kDisconnectRequestTimeout|
  // from the last disconnect request time then treat it as a user
  // initiated disconnect and skip histogram log.
  if (time_since_disconnect_requested &&
      time_since_disconnect_requested < kDisconnectRequestTimeout) {
    return;
  }
  LogCellularDisconnectionsHistogram(ConnectionState::kDisconnected,
                                     GetSimType(network),
                                     network->IsManagedByPolicy());
}

void CellularMetricsLogger::CheckForESimProfileStatusMetric() {
  if (!cellular_esim_profile_handler_ || !is_cellular_available_ ||
      is_esim_profile_status_logged_ || !IsLoggedInUserRegular()) {
    return;
  }

  std::vector<CellularESimProfile> esim_profiles =
      cellular_esim_profile_handler_->GetESimProfiles();

  bool pending_profiles_exist = false;
  bool active_profiles_exist = false;
  for (const auto& profile : esim_profiles) {
    switch (profile.state()) {
      case CellularESimProfile::State::kPending:
        [[fallthrough]];
      case CellularESimProfile::State::kInstalling:
        pending_profiles_exist = true;
        break;

      case CellularESimProfile::State::kInactive:
        [[fallthrough]];
      case CellularESimProfile::State::kActive:
        active_profiles_exist = true;
        break;
    }
  }

  ESimProfileStatus activation_state;
  if (active_profiles_exist && !pending_profiles_exist)
    activation_state = ESimProfileStatus::kActive;
  else if (active_profiles_exist && pending_profiles_exist)
    activation_state = ESimProfileStatus::kActiveWithPendingProfiles;
  else if (!active_profiles_exist && pending_profiles_exist)
    activation_state = ESimProfileStatus::kPendingProfilesOnly;
  else
    activation_state = ESimProfileStatus::kNoProfiles;

  UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.StatusAtLogin",
                            activation_state);
  is_esim_profile_status_logged_ = true;
}

void CellularMetricsLogger::CheckForPSimActivationStateMetric() {
  if (!is_cellular_available_ || is_psim_activation_state_logged_ ||
      !IsLoggedInUserRegular()) {
    return;
  }

  NetworkStateHandler::NetworkStateList network_list;
  network_state_handler_->GetVisibleNetworkListByType(
      NetworkTypePattern::Cellular(), &network_list);

  if (network_list.size() == 0)
    return;

  std::optional<std::string> psim_activation_state;
  for (const auto* network : network_list) {
    if (GetSimType(network) == SimType::kPSim)
      psim_activation_state = network->activation_state();
  }

  // No PSim networks exist.
  if (!psim_activation_state.has_value())
    return;

  UMA_HISTOGRAM_ENUMERATION("Network.Cellular.PSim.StatusAtLogin",
                            PSimActivationStateToEnum(*psim_activation_state));
  is_psim_activation_state_logged_ = true;
}

void CellularMetricsLogger::CheckForCellularServiceCountMetric() {
  if (!is_cellular_available_ || is_service_count_logged_ ||
      !IsLoggedInUserRegular()) {
    return;
  }

  NetworkStateHandler::NetworkStateList network_list;
  network_state_handler_->GetVisibleNetworkListByType(
      NetworkTypePattern::Cellular(), &network_list);

  size_t psim_networks = 0;
  size_t esim_profiles = 0;
  size_t esim_policy_profiles = 0;

  for (const auto* network : network_list) {
    SimType sim_type = GetSimType(network);
    if (sim_type == SimType::kPSim) {
      psim_networks++;
    } else {
      esim_profiles++;
      if (network->IsManagedByPolicy())
        esim_policy_profiles++;
    }
  }

  if (managed_network_configuration_handler_->AllowCellularSimLock()) {
    UMA_HISTOGRAM_COUNTS_100(
        "Network.Cellular.PSim.ServiceAtLoginCount.SimLockAllowedByPolicy",
        psim_networks);
    UMA_HISTOGRAM_COUNTS_100(
        "Network.Cellular.ESim.ServiceAtLoginCount.SimLockAllowedByPolicy",
        esim_profiles);
  } else {
    UMA_HISTOGRAM_COUNTS_100(
        "Network.Cellular.PSim.ServiceAtLoginCount.SimLockProhibitedByPolicy",
        psim_networks);
    UMA_HISTOGRAM_COUNTS_100(
        "Network.Cellular.ESim.ServiceAtLoginCount.SimLockProhibitedByPolicy",
        esim_profiles);
  }

  UMA_HISTOGRAM_COUNTS_100("Network.Cellular.ESim.ServiceAtLoginCount",
                           esim_profiles);

  UMA_HISTOGRAM_COUNTS_100("Network.Cellular.ESim.Policy.ServiceAtLogin.Count",
                           esim_policy_profiles);
  is_service_count_logged_ = true;
}

void CellularMetricsLogger::CheckForApnPolicyMetric() {
  if (is_apn_policy_logged_) {
    return;
  }

  base::UmaHistogramBoolean(
      "Network.Ash.Cellular.Apn.Login.AllowApnModification",
      managed_network_configuration_handler_->AllowApnModification());
  is_apn_policy_logged_ = true;
}

void CellularMetricsLogger::CheckForCellularUsageMetrics() {
  if (!is_cellular_available_)
    return;

  NetworkStateHandler::NetworkStateList network_list;
  network_state_handler_->GetVisibleNetworkListByType(
      NetworkTypePattern::NonVirtual(), &network_list);

  std::optional<const NetworkState*> connected_cellular_network;
  bool is_non_cellular_connected = false;
  for (auto* network : network_list) {
    if (!network->IsConnectedState())
      continue;

    // Note: Only one cellular network may be ever connected.
    if (network->Matches(NetworkTypePattern::Cellular()))
      connected_cellular_network = network;
    else
      is_non_cellular_connected = true;
  }

  // Discard not-connected states received before the timer runs out.
  if (!connected_cellular_network.has_value() &&
      initialization_timer_.IsRunning()) {
    return;
  }

  CellularUsage usage;
  std::optional<SimType> sim_type;
  bool is_managed_by_policy = false;
  if (connected_cellular_network.has_value()) {
    usage = is_non_cellular_connected
                ? CellularUsage::kConnectedWithOtherNetwork
                : CellularUsage::kConnectedAndOnlyNetwork;
    sim_type = GetSimType(connected_cellular_network.value());
    is_managed_by_policy =
        connected_cellular_network.value()->IsManagedByPolicy();
  } else {
    usage = CellularUsage::kNotConnected;
  }

  if (!sim_type.has_value() || *sim_type == SimType::kPSim) {
    if (usage != last_psim_cellular_usage_) {
      UMA_HISTOGRAM_ENUMERATION("Network.Cellular.PSim.Usage.Count", usage);
      if (last_psim_cellular_usage_ ==
          CellularUsage::kConnectedAndOnlyNetwork) {
        UMA_HISTOGRAM_LONG_TIMES("Network.Cellular.PSim.Usage.Duration",
                                 psim_usage_elapsed_timer_->Elapsed());
      }
    }

    psim_usage_elapsed_timer_ = base::ElapsedTimer();
    last_psim_cellular_usage_ = usage;
  }

  if (!sim_type.has_value() || *sim_type == SimType::kESim) {
    if (usage != last_esim_cellular_usage_) {
      UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.Usage.Count", usage);
      // Logs to ESim.Policy.Usage.Count histogram when the current connected
      // network is managed by policy or previous connected managed cellular
      // network gets disconnected.
      if (is_managed_by_policy ||
          (last_managed_by_policy_ && usage == CellularUsage::kNotConnected)) {
        UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.Policy.Usage.Count",
                                  usage);
      }

      if (last_esim_cellular_usage_ ==
          CellularUsage::kConnectedAndOnlyNetwork) {
        const base::TimeDelta usage_duration =
            esim_usage_elapsed_timer_->Elapsed();

        UMA_HISTOGRAM_LONG_TIMES("Network.Cellular.ESim.Usage.Duration",
                                 usage_duration);
        if (last_managed_by_policy_) {
          UMA_HISTOGRAM_LONG_TIMES(
              "Network.Cellular.ESim.Policy.Usage.Duration", usage_duration);
        }
      }
    }

    HandleESimFeatureUsageChange(
        last_esim_cellular_usage_.value_or(CellularUsage::kNotConnected), usage,
        is_managed_by_policy);

    esim_usage_elapsed_timer_ = base::ElapsedTimer();
    last_esim_cellular_usage_ = usage;
    last_managed_by_policy_ = is_managed_by_policy;
  }
}

void CellularMetricsLogger::HandleESimFeatureUsageChange(
    CellularUsage last_usage,
    CellularUsage current_usage,
    bool is_managed_by_policy) {
  if (!esim_feature_usage_metrics_ || last_usage == current_usage) {
    return;
  }

  // If the user first connects to an eSIM cellular network, regardless if
  // another network type is connected, record a successful usage. Note that the
  // preference order is Ethernet > Wifi > Cellular. Also note that
  // RecordUsage() should only be called when the usage state transitions from a
  // not connected state (kNotConnected) to a connected state
  // (kConnectedAndOnlyNetwork or kConnectedWithOtherNetwork). I.e RecordUsage()
  // should not be called when the usage state transitions from
  // kConnectedAndOnlyNetwork to kConnectedWithOtherNetwork, and vice versa.
  if (last_usage == CellularUsage::kNotConnected) {
    esim_feature_usage_metrics_->RecordUsage(/*success=*/true);
    if (is_managed_by_policy && enterprise_esim_feature_usage_metrics_) {
      enterprise_esim_feature_usage_metrics_->RecordUsage(/*success=*/true);
    }
  }

  // If the user is actively using the eSIM cellular network, start recording
  // usage time.
  if (current_usage == CellularUsage::kConnectedAndOnlyNetwork) {
    esim_feature_usage_metrics_->StartUsage();
    if (is_managed_by_policy && enterprise_esim_feature_usage_metrics_) {
      enterprise_esim_feature_usage_metrics_->StartUsage();
    }
  }

  // If the user is no longer actively using the eSIM cellular network, stop
  // recording usage time.
  if (last_usage == CellularUsage::kConnectedAndOnlyNetwork) {
    esim_feature_usage_metrics_->StopUsage();
    if (last_managed_by_policy_ && enterprise_esim_feature_usage_metrics_) {
      enterprise_esim_feature_usage_metrics_->StopUsage();
    }
  }
}

CellularMetricsLogger::ConnectionInfo*
CellularMetricsLogger::GetConnectionInfoForCellularNetwork(
    const std::string& cellular_network_guid) {
  auto it = guid_to_connection_info_map_.find(cellular_network_guid);

  ConnectionInfo* connection_info;
  if (it == guid_to_connection_info_map_.end()) {
    // We could get connection events in some cases before network
    // list change event. Insert new network into the list.
    auto insert_result = guid_to_connection_info_map_.insert_or_assign(
        cellular_network_guid,
        std::make_unique<ConnectionInfo>(cellular_network_guid));
    connection_info = insert_result.first->second.get();
  } else {
    connection_info = it->second.get();
  }

  return connection_info;
}

void CellularMetricsLogger::OnShuttingDown() {
  network_state_handler_observer_.Reset();
  network_state_handler_ = nullptr;
  enterprise_esim_feature_usage_metrics_.reset();
  esim_feature_usage_metrics_.reset();
}

}  // namespace ash