// 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.
#ifndef CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_METRICS_LOGGER_H_
#define CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_METRICS_LOGGER_H_
#include <optional>
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_connection_observer.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
namespace ash {
class CellularESimProfileHandler;
class CellularMetricsLoggerTest;
class ESimFeatureUsageMetrics;
class EnterpriseESimFeatureUsageMetrics;
class NetworkConnectionHandler;
class NetworkState;
// Cellular network SIM types.
enum class SimType {
kPSim,
kESim,
};
// Class for tracking cellular network related metrics.
//
// This class adds observers on network state and makes the following
// measurements on cellular networks:
// 1. Time to connected.
// 2. Connected states and non-user initiated disconnections.
// 3. Activation status at login.
// 4. Cellular network usage type.
//
// Note: This class does not start logging metrics until Init() is
// invoked.
class COMPONENT_EXPORT(CHROMEOS_NETWORK) CellularMetricsLogger
: public NetworkStateHandlerObserver,
public LoginState::Observer,
public NetworkConnectionObserver {
public:
// Histograms associated with SIM Pin operations.
static const char kSimPinChangeSuccessHistogram[];
static const char kManagedSimPinUnblockSuccessHistogram[];
static const char kManagedSimPinUnlockSuccessHistogram[];
static const char kSimPinRemoveLockSuccessHistogram[];
static const char kSimPinRequireLockSuccessHistogram[];
static const char kRestrictedSimPinUnblockSuccessHistogram[];
static const char kRestrictedSimPinUnlockSuccessHistogram[];
static const char kUnmanagedSimPinUnblockSuccessHistogram[];
static const char kUnmanagedSimPinUnlockSuccessHistogram[];
static const char kUnrestrictedSimPinUnblockSuccessHistogram[];
static const char kUnrestrictedSimPinUnlockSuccessHistogram[];
static const char kChangePinSuccessSimPinLockPolicyHistogram[];
static const char kRequirePinSuccessSimPinLockPolicyHistogram[];
// Histograms associated with user initiated connection success.
static const char kESimUserInitiatedConnectionResultHistogram[];
static const char kPSimUserInitiatedConnectionResultHistogram[];
// Histograms associated with all connection success.
static const char kESimAllConnectionResultHistogram[];
static const char kESimPolicyAllConnectionResultHistogram[];
static const char kPSimAllConnectionResultHistogram[];
// Histograms associated with SIM Lock notification events.
static const char kSimLockNotificationEventHistogram[];
static const char kSimLockNotificationLockType[];
// The amount of time after cellular device is added to device list,
// after which cellular device is considered initialized.
static const base::TimeDelta kInitializationTimeout;
// Histograms associated with SIM Lock status on the active network.
static const char kUnrestrictedActiveNetworkSIMLockStatus[];
static const char kRestrictedActiveNetworkSIMLockStatus[];
// PIN operations that are tracked by metrics.
enum class SimPinOperation {
kRequireLock = 0,
kRemoveLock = 1,
kUnlock = 2,
kUnblock = 3,
kChange = 4,
};
// SIM lock notification events
enum class SimLockNotificationEvent {
kShown = 0,
kClicked = 1,
kDismissed = 2,
kMaxValue = kDismissed
};
// SIM pin lock type.
enum class SimPinLockType {
kPinLocked = 0,
kPukLocked = 1,
kUnlocked = 2,
kCarrierLocked = 3,
kMaxValue = kCarrierLocked
};
// Records the result of pin operations performed.
static void RecordSimPinOperationResult(
const SimPinOperation& pin_operation,
const bool allow_cellular_sim_lock,
const std::optional<std::string>& shill_error_name = std::nullopt);
// Records the SIM lock notification event.
static void RecordSimLockNotificationEvent(
const SimLockNotificationEvent notification_event);
// Records the SIM lock type when the notification is surfaced.
static void RecordSimLockNotificationLockType(
const std::string& sim_lock_type);
CellularMetricsLogger();
CellularMetricsLogger(const CellularMetricsLogger&) = delete;
CellularMetricsLogger& operator=(const CellularMetricsLogger&) = delete;
~CellularMetricsLogger() override;
void Init(NetworkStateHandler* network_state_handler,
NetworkConnectionHandler* network_connection_handler,
CellularESimProfileHandler* cellular_esim_profile_handler,
ManagedNetworkConfigurationHandler*
managed_network_configuration_handler);
// LoginState::Observer:
void LoggedInStateChanged() override;
// NetworkStateHandlerObserver::
void DeviceListChanged() override;
void NetworkListChanged() override;
void NetworkConnectionStateChanged(const NetworkState* network) override;
void OnShuttingDown() override;
// NetworkConnectionObserver::
void ConnectSucceeded(const std::string& service_path) override;
void ConnectFailed(const std::string& service_path,
const std::string& error_name) override;
void DisconnectRequested(const std::string& service_path) override;
private:
friend class CellularMetricsLoggerTest;
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
DuplicateCellularServiceGuids);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest, CellularConnectResult);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CancellationDuringConnecting);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
UserInitiatedConnectionResult);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CellularESimProfileStatusAtLoginTest);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest, CellularUsageCountTest);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CellularUsageCountDongleTest);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CellularPSimActivationStateAtLoginTest);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CellularTimeToConnectedTest);
FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
CellularDisconnectionsTest);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, RequirePin);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, EnterPinOnUnmanagedDevice);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, EnterPinOnManagedDevice);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest,
UnblockPinOnUnmanagedDevice);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, UnblockPinOnManagedDevice);
FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, ChangePin);
// The amount of time after a disconnect request within which any
// disconnections are considered user initiated.
static const base::TimeDelta kDisconnectRequestTimeout;
// Stores connection related information for a cellular network.
struct ConnectionInfo {
explicit ConnectionInfo(const std::string& network_guid);
ConnectionInfo(const std::string& network_guid,
bool is_connected,
bool is_connecting);
~ConnectionInfo();
const std::string network_guid;
std::optional<bool> is_connected;
bool is_connecting = false;
// Tracks whether a disconnect was requested from chrome on a network that
// was previously in the connecting state. This field is set back to false
// when shill connection failures are checked in
// NetworkConnectionStateChanged().
bool disconnect_requested = false;
std::optional<base::TimeTicks> last_disconnect_request_time;
std::optional<base::TimeTicks> last_connect_start_time;
};
// Usage type for cellular network. These values are persisted to logs.
// Entries should not be renumbered and numeric values should never
// be reused.
enum class CellularUsage {
kConnectedAndOnlyNetwork = 0,
kConnectedWithOtherNetwork = 1,
kNotConnected = 2,
kMaxValue = kNotConnected
};
// Activation state for PSim cellular network.
// These values are persisted to logs. Entries should not be renumbered
// and numeric values should never be reused.
enum class PSimActivationState {
kActivated = 0,
kActivating = 1,
kNotActivated = 2,
kPartiallyActivated = 3,
kUnknown = 4,
kMaxValue = kUnknown
};
// Profile status for eSIM cellular network.
// These values are persisted to logs. Entries should not be renumbered
// and numeric values should never be reused.
enum class ESimProfileStatus {
kActive = 0,
kActiveWithPendingProfiles = 1,
kPendingProfilesOnly = 2,
kNoProfiles = 3,
kMaxValue = kNoProfiles
};
// Cellular connection state. These values are persisted to logs.
// Entries should not be renumbered and numeric values should
// never be reused.
enum class ConnectionState {
kConnected = 0,
kDisconnected = 1,
kMaxValue = kDisconnected
};
// Result of PIN operations.
// These values are persisted to logs. Entries should not be renumbered
// and numeric values should never be reused.
// Note: With the exception of Success, enums should match the
// error names listed near the top of NetworkDeviceHandler.
enum class SimPinOperationResult {
kSuccess = 0,
kErrorDeviceMissing = 1,
kErrorFailure = 2,
kErrorIncorrectPin = 3,
kErrorNotFound = 4,
kErrorNotSupported = 5,
kErrorPinBlocked = 6,
kErrorPinRequired = 7,
kErrorTimeout = 8,
kErrorUnknown = 9,
kErrorWrongState = 10,
kMaxValue = kErrorWrongState,
};
// This enum is used to track the connection results from
// NetworkConnectionHandler. With the exception of kSuccess and kUnknown,
// these enums are mapped to relevant NetworkConnectionHandler errors
// associated to user initiated connection errors.
// These values are persisted to logs. Entries should not be renumbered
// and numeric values should never be reused.
enum class ConnectResult {
kSuccess = 0,
kUnknown = 1,
kInvalidGuid = 2,
kInvalidState = 3,
kCanceled = 4,
kNotConfigured = 5,
kBlocked = 6,
kCellularInhibitFailure = 7,
kESimProfileIssue = 8,
kCellularOutOfCredits = 9,
kSimPinPukLocked = 10,
kConnectFailed = 11,
kNotConnected = 12,
kActivateFailed = 13,
kEnabledOrDisabledWhenNotAvailable = 14,
kErrorCellularDeviceBusy = 15,
kErrorConnectTimeout = 16,
kConnectableCellularTimeout = 17,
kSimCarrierLocked = 18,
kMaxValue = kSimCarrierLocked,
};
// Result of state changes to a cellular network triggered by any connection
// attempt. With the exception of kSuccess and kUnknown, these enums are
// mapped directly to Shill errors. These values are persisted to logs.
// Entries should not be renumbered and numeric values should never be reused.
enum class ShillConnectResult {
kSuccess = 0,
kUnknown = 1,
kFailedToConnect = 2,
kDhcpFailure = 3,
kDnsLookupFailure = 4,
kEapAuthentication = 5,
kEapLocalTls = 6,
kEapRemoteTls = 7,
kOutOfRange = 8,
kPinMissing = 9,
kNoFailure = 10,
kNotAssociated = 11,
kNotAuthenticated = 12,
kTooManySTAs = 13,
kBadPassphrase = 14,
kBadWepKey = 15,
kErrorSimPinPukLocked = 16,
kErrorNotRegistered = 17,
kErrorSimCarrierLocked = 18,
kMaxValue = kErrorSimCarrierLocked,
};
// Convert shill error name string to SimPinOperationResult enum.
static SimPinOperationResult GetSimPinOperationResultForShillError(
const std::string& shill_error_name);
// Converts a NetworkConnectionHandler string error to a ConnectResult enum.
static ConnectResult NetworkConnectionErrorToConnectResult(
const std::string& error_name);
// Converts a Shill error string to a ShillConnectResult enum.
static ShillConnectResult ShillErrorToConnectResult(
const std::string& error_name);
static void LogCellularUserInitiatedConnectionSuccessHistogram(
ConnectResult start_connect_result,
SimType sim_type);
// Returns null if there is no network with the given path or if the
// network is non-cellular.
const NetworkState* GetCellularNetwork(const std::string& service_path);
// Convert shill activation state string to PSimActivationState enum
PSimActivationState PSimActivationStateToEnum(const std::string& state);
// Helper method to save cellular disconnections histogram.
void LogCellularDisconnectionsHistogram(ConnectionState connection_state,
SimType sim_type,
bool is_managed_by_policy);
void LogCellularAllConnectionSuccessHistogram(
ShillConnectResult start_connect_result,
SimType sim_type,
bool is_managed_by_policy);
void OnInitializationTimeout();
// Tracks cellular network connection state and logs time to connected.
void CheckForTimeToConnectedMetric(const NetworkState* network);
// Tracks current cellular connection status and logs the metric.
// Current connection can be in one of the three states:
// (Connected/PIN_Locked/PUK_Blocked). This will be logged when a
// switch happens from no connection to an active connection or
// from one connection to another.
void CheckForSIMStatusMetric(const NetworkState* network);
// Tracks cellular network connected states and non user initiated
// disconnections.
void CheckForConnectionStateMetric(const NetworkState* network);
// Tracks the activation state of the PSim cellular network if available and
// if |is_psim_activation_state_logged_| is false.
void CheckForPSimActivationStateMetric();
// Tracks the activation state of eSIM cellular networks if available and
// if |is_esim_profile_status_logged_| is false.
void CheckForESimProfileStatusMetric();
// Tracks failed connection attempts.
void CheckForShillConnectionFailureMetric(const NetworkState* network);
// This checks the state of connected networks and logs
// cellular network usage histogram. Histogram is only logged
// when usage state changes.
void CheckForCellularUsageMetrics();
// Tracks how many eSIM profiles are installed on the device and how many pSIM
// networks are available on the device if |is_service_count_logged_| is true.
void CheckForCellularServiceCountMetric();
// Tracks the state of the Allow APN Modification policy at login.
void CheckForApnPolicyMetric();
// Handles eSIM Standard Feature Usage Logging metrics when the cellular usage
// changes for an eSIM network.
void HandleESimFeatureUsageChange(CellularUsage last_esim_cellular_usage,
CellularUsage current_usage,
bool is_managed_by_policy);
// Returns the ConnectionInfo for given |cellular_network_guid|.
ConnectionInfo* GetConnectionInfoForCellularNetwork(
const std::string& cellular_network_guid);
// Tracks the last cellular network usage state.
std::optional<CellularUsage> last_cellular_usage_;
// Tracks the last PSim cellular network usage state.
std::optional<CellularUsage> last_psim_cellular_usage_;
// Tracks the last time the PSim network's cellular usage changed.
std::optional<base::ElapsedTimer> psim_usage_elapsed_timer_;
// Tracks the last eSIM cellular network usage state.
std::optional<CellularUsage> last_esim_cellular_usage_;
// Tracks the last time eSIM network's cellular usage is managed or not.
bool last_managed_by_policy_ = false;
// Tracks the last time the eSIM network's cellular usage changed.
std::optional<base::ElapsedTimer> esim_usage_elapsed_timer_;
// Tracks whether cellular device is available or not.
bool is_cellular_available_ = false;
raw_ptr<NetworkStateHandler> network_state_handler_ = nullptr;
base::ScopedObservation<NetworkStateHandler, NetworkStateHandlerObserver>
network_state_handler_observer_{this};
raw_ptr<ManagedNetworkConfigurationHandler>
managed_network_configuration_handler_ = nullptr;
raw_ptr<NetworkConnectionHandler> network_connection_handler_ = nullptr;
raw_ptr<CellularESimProfileHandler> cellular_esim_profile_handler_ = nullptr;
// A timer to wait for cellular initialization. This is useful
// to avoid tracking intermediate states when cellular network is
// starting up.
base::OneShotTimer initialization_timer_;
// Stores the iccid of the most recently active network.
std::string last_active_network_iccid_;
// Tracks whether the PSim activation state is already logged for this
// session.
bool is_psim_activation_state_logged_ = false;
// Tracks whether the eSIM profile status is already logged for this
// session.
bool is_esim_profile_status_logged_ = false;
// Tracks whether service count is already logged for this session.
bool is_service_count_logged_ = false;
// Tracks whether apn policy state is already logged for this session.
bool is_apn_policy_logged_ = false;
// Stores connection information for all cellular networks.
base::flat_map<std::string, std::unique_ptr<ConnectionInfo>>
guid_to_connection_info_map_;
bool initialized_ = false;
// Tracks eSIM feature usage for the Standard Feature Usage Logging Framework.
std::unique_ptr<ESimFeatureUsageMetrics> esim_feature_usage_metrics_;
// Tracks enterprise eSIM feature usage for the Standard Feature Usage Logging
// Framework.
std::unique_ptr<EnterpriseESimFeatureUsageMetrics>
enterprise_esim_feature_usage_metrics_;
};
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_NETWORK_CELLULAR_METRICS_LOGGER_H_