blob: baf432beb92ae559b0c76fb86a8c5240df955e4c [file] [log] [blame] [edit]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/metrics.h"
#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <base/check.h>
#include <base/containers/contains.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/rand_util.h>
#include <base/strings/strcat.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/dbus/shill/dbus-constants.h>
#include <crypto/sha2.h>
#include <metrics/bootstat.h>
#include <metrics/structured_events.h>
#include "shill/cellular/apn_list.h"
#include "shill/cellular/cellular_consts.h"
#include "shill/logging.h"
#include "shill/network/connection_diagnostics.h"
#include "shill/vpn/vpn_types.h"
#include "shill/wifi/wifi_metrics_utils.h"
#include "shill/wifi/wifi_service.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kMetrics;
} // namespace Logging
namespace {
// Name prefix used for Shill UMA metrics whose names are generated
// dynamically at event recording time.
constexpr char kMetricPrefix[] = "Network.Shill";
// Length of the random salt used to pseudonymize logs.
constexpr int kPseudoTagSaltLen = 32;
// How many bytes of the hash are printed.
constexpr int kPseudoTagHashLen = 8;
bool IsInvalidTag(uint64_t tag) {
return tag == WiFiService::kSessionTagInvalid;
}
int64_t GetMicroSecondsMonotonic() {
// base::TimeTicks doesn't provide a method to convert to the internal value.
// So we get the base::TimeDelta by subtracting epoch first, then convert it
// to the microseconds.
return (base::TimeTicks::Now() - base::TimeTicks::UnixEpoch())
.InMicroseconds();
}
Metrics::CellularConnectResult ConvertErrorToCellularConnectResult(
const Error::Type& error) {
switch (error) {
case Error::kSuccess:
return Metrics::CellularConnectResult::kCellularConnectResultSuccess;
case Error::kWrongState:
return Metrics::CellularConnectResult::kCellularConnectResultWrongState;
case Error::kOperationFailed:
return Metrics::CellularConnectResult::
kCellularConnectResultOperationFailed;
case Error::kAlreadyConnected:
return Metrics::CellularConnectResult::
kCellularConnectResultAlreadyConnected;
case Error::kNotRegistered:
return Metrics::CellularConnectResult::
kCellularConnectResultNotRegistered;
case Error::kNotOnHomeNetwork:
return Metrics::CellularConnectResult::
kCellularConnectResultNotOnHomeNetwork;
case Error::kIncorrectPin:
return Metrics::CellularConnectResult::kCellularConnectResultIncorrectPin;
case Error::kPinRequired:
return Metrics::CellularConnectResult::kCellularConnectResultPinRequired;
case Error::kPinBlocked:
return Metrics::CellularConnectResult::kCellularConnectResultPinBlocked;
case Error::kInvalidApn:
return Metrics::CellularConnectResult::kCellularConnectResultInvalidApn;
case Error::kInternalError:
return Metrics::CellularConnectResult::
kCellularConnectResultInternalError;
default:
LOG(WARNING) << "Unexpected error type: " << error;
return Metrics::CellularConnectResult::kCellularConnectResultUnknown;
}
}
// Converts VPN types to strings used in a metric name.
std::string VPNTypeToMetricString(VPNType type) {
switch (type) {
case VPNType::kARC:
return "ARC";
case VPNType::kIKEv2:
return "Ikev2";
case VPNType::kL2TPIPsec:
return "L2tpIpsec";
case VPNType::kOpenVPN:
return "OpenVPN";
case VPNType::kThirdParty:
return "ThirdParty";
case VPNType::kWireGuard:
return "WireGuard";
}
NOTREACHED();
return "";
}
std::string GetApnTypeString(
Metrics::DetailedCellularConnectionResult::APNType apn_type) {
switch (apn_type) {
case Metrics::DetailedCellularConnectionResult::APNType::kDefault:
return kApnTypeDefault;
case Metrics::DetailedCellularConnectionResult::APNType::kAttach:
return kApnTypeIA;
case Metrics::DetailedCellularConnectionResult::APNType::kDUN:
return kApnTypeDun;
}
}
} // namespace
Metrics::Metrics()
: library_(&metrics_library_),
time_suspend_actions_timer(new chromeos_metrics::Timer),
time_between_rekey_and_connection_failure_timer_(
new chromeos_metrics::Timer) {
char salt[kPseudoTagSaltLen];
base::RandBytes(base::as_writable_byte_span(salt));
pseudo_tag_salt_ = std::string(salt, kPseudoTagSaltLen);
}
Metrics::~Metrics() = default;
void Metrics::SendEnumToUMA(const EnumMetric<FixedName>& metric, int sample) {
// The std::string conversion should be removed once MetricsLibraryInterface
// is improved with std::string_view variants of Send*ToUMA.
library_->SendEnumToUMA(std::string(metric.n.name), sample, metric.max);
}
void Metrics::SendEnumToUMA(const EnumMetric<NameByApnType>& metric,
DetailedCellularConnectionResult::APNType type,
int sample) {
// Using the format Network.Shill.Cellular.{MetricName}.{ApnType} to make it
// easier to find the metrics using autocomplete in UMA.
const std::string name =
base::StrCat({kMetricPrefix, ".Cellular.", metric.n.name, ".",
GetApnTypeString(type)});
library_->SendEnumToUMA(name, sample, metric.max);
}
void Metrics::SendEnumToUMA(const EnumMetric<NameByTechnology>& metric,
Technology tech,
int sample) {
library_->SendEnumToUMA(
GetFullMetricName(metric.n.name, tech, metric.n.location), sample,
metric.max);
}
void Metrics::SendEnumToUMA(const EnumMetric<NameByVPNType>& metric,
VPNType type,
int sample) {
const std::string name =
base::StrCat({kMetricPrefix, ".Vpn.", VPNTypeToMetricString(type), ".",
metric.n.name});
library_->SendEnumToUMA(name, sample, metric.max);
}
void Metrics::SendEnumToUMA(const EnumMetric<PrefixName>& metric,
const std::string& suffix,
int sample) {
library_->SendEnumToUMA(base::StrCat({metric.n.prefix, suffix}), sample,
metric.max);
}
void Metrics::SendToUMA(const Metrics::HistogramMetric<FixedName>& metric,
int sample) {
// The std::string conversion should be removed once MetricsLibraryInterface
// is improved with std::string_view variants of Send*ToUMA.
library_->SendToUMA(std::string(metric.n.name), sample, metric.min,
metric.max, metric.num_buckets);
}
void Metrics::SendToUMA(
const Metrics::HistogramMetric<NameByTechnology>& metric,
Technology tech,
int sample) {
library_->SendToUMA(GetFullMetricName(metric.n.name, tech, metric.n.location),
sample, metric.min, metric.max, metric.num_buckets);
}
void Metrics::SendToUMA(const Metrics::HistogramMetric<PrefixName>& metric,
const std::string& suffix,
int sample) {
library_->SendToUMA(base::StrCat({metric.n.prefix, suffix}), sample,
metric.min, metric.max, metric.num_buckets);
}
void Metrics::SendToUMA(const HistogramMetric<NameByVPNType>& metric,
VPNType type,
int sample) {
const std::string name =
base::StrCat({kMetricPrefix, ".Vpn.", VPNTypeToMetricString(type), ".",
metric.n.name});
library_->SendToUMA(name, sample, metric.min, metric.max, metric.num_buckets);
}
void Metrics::SendSparseToUMA(const SparseMetric<FixedName>& metric,
int sample) {
// The std::string conversion should be removed once MetricsLibraryInterface
// is improved with std::string_view variants of Send*ToUMA.
library_->SendSparseToUMA(std::string(metric.n.name), sample);
}
void Metrics::SendSparseToUMA(const SparseMetric<NameByTechnology>& metric,
Technology technology,
int sample) {
library_->SendSparseToUMA(
GetFullMetricName(metric.n.name, technology, metric.n.location), sample);
}
// static
Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16_t frequency) {
WiFiChannel channel = kWiFiChannelUndef;
if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel2412 +
(frequency - kWiFiFrequency2412) /
kWiFiBandwidth5MHz);
} else if (frequency == kWiFiFrequency2484) {
channel = kWiFiChannel2484;
} else if (kWiFiFrequency5170 <= frequency &&
frequency <= kWiFiFrequency5230) {
if ((frequency % kWiFiBandwidth20MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel5180 +
(frequency - kWiFiFrequency5180) /
kWiFiBandwidth20MHz);
if ((frequency % kWiFiBandwidth20MHz) == 10)
channel = static_cast<WiFiChannel>(kWiFiChannel5170 +
(frequency - kWiFiFrequency5170) /
kWiFiBandwidth20MHz);
} else if (kWiFiFrequency5240 <= frequency &&
frequency <= kWiFiFrequency5320) {
if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel5180 +
(frequency - kWiFiFrequency5180) /
kWiFiBandwidth20MHz);
} else if (kWiFiFrequency5500 <= frequency &&
frequency <= kWiFiFrequency5700) {
if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel5500 +
(frequency - kWiFiFrequency5500) /
kWiFiBandwidth20MHz);
} else if (kWiFiFrequency5745 <= frequency &&
frequency <= kWiFiFrequency5825) {
if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel5745 +
(frequency - kWiFiFrequency5745) /
kWiFiBandwidth20MHz);
} else if (kWiFiFrequency5955 <= frequency &&
frequency <= kWiFiFrequency7115) {
if (((frequency - kWiFiFrequency5955) % kWiFiBandwidth20MHz) == 0)
channel = static_cast<WiFiChannel>(kWiFiChannel5955 +
(frequency - kWiFiFrequency5955) /
kWiFiBandwidth20MHz);
}
CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
if (channel == kWiFiChannelUndef)
LOG(WARNING) << "no mapping for frequency " << frequency;
else
SLOG(3) << "mapped frequency " << frequency << " to enum bucket "
<< channel;
return channel;
}
// static
Metrics::WiFiFrequencyRange Metrics::WiFiChannelToFrequencyRange(
Metrics::WiFiChannel channel) {
if (channel >= kWiFiChannelMin24 && channel <= kWiFiChannelMax24) {
return kWiFiFrequencyRange24;
} else if (channel >= kWiFiChannelMin5 && channel <= kWiFiChannelMax5) {
return kWiFiFrequencyRange5;
} else if (channel >= kWiFiChannelMin6 && channel <= kWiFiChannelMax6) {
return kWiFiFrequencyRange6;
} else {
return kWiFiFrequencyRangeUndef;
}
}
// static
Metrics::EapOuterProtocol Metrics::EapOuterProtocolStringToEnum(
const std::string& outer) {
if (outer == kEapMethodPEAP) {
return kEapOuterProtocolPeap;
} else if (outer == kEapMethodTLS) {
return kEapOuterProtocolTls;
} else if (outer == kEapMethodTTLS) {
return kEapOuterProtocolTtls;
} else if (outer == kEapMethodLEAP) {
return kEapOuterProtocolLeap;
} else {
return kEapOuterProtocolUnknown;
}
}
// static
Metrics::EapInnerProtocol Metrics::EapInnerProtocolStringToEnum(
const std::string& inner) {
if (inner.empty()) {
return kEapInnerProtocolNone;
} else if (inner == kEapPhase2AuthPEAPMD5) {
return kEapInnerProtocolPeapMd5;
} else if (inner == kEapPhase2AuthPEAPMSCHAPV2) {
return kEapInnerProtocolPeapMschapv2;
} else if (inner == kEapPhase2AuthTTLSEAPMD5) {
return kEapInnerProtocolTtlsEapMd5;
} else if (inner == kEapPhase2AuthTTLSEAPMSCHAPV2) {
return kEapInnerProtocolTtlsEapMschapv2;
} else if (inner == kEapPhase2AuthTTLSMSCHAPV2) {
return kEapInnerProtocolTtlsMschapv2;
} else if (inner == kEapPhase2AuthTTLSMSCHAP) {
return kEapInnerProtocolTtlsMschap;
} else if (inner == kEapPhase2AuthTTLSPAP) {
return kEapInnerProtocolTtlsPap;
} else if (inner == kEapPhase2AuthTTLSCHAP) {
return kEapInnerProtocolTtlsChap;
} else {
return kEapInnerProtocolUnknown;
}
}
// static
std::string Metrics::GetFullMetricName(std::string_view metric_name,
Technology technology_id,
TechnologyLocation location) {
std::string technology = TechnologyName(technology_id);
technology[0] = base::ToUpperASCII(technology[0]);
if (location == TechnologyLocation::kBeforeName) {
return base::StrCat({kMetricPrefix, ".", technology, ".", metric_name});
} else {
return base::StrCat({kMetricPrefix, ".", metric_name, ".", technology});
}
}
void Metrics::NotifySuspendActionsStarted() {
if (time_suspend_actions_timer->HasStarted())
return;
time_suspend_actions_timer->Start();
}
void Metrics::NotifySuspendActionsCompleted(bool success) {
if (!time_suspend_actions_timer->HasStarted())
return;
base::TimeDelta elapsed_time;
time_suspend_actions_timer->GetElapsedTime(&elapsed_time);
time_suspend_actions_timer->Reset();
SendToUMA(kMetricSuspendActionTimeTaken, elapsed_time.InMilliseconds());
}
void Metrics::NotifyApChannelSwitch(uint16_t frequency,
uint16_t new_frequency) {
WiFiChannel channel = WiFiFrequencyToChannel(frequency);
WiFiChannel new_channel = WiFiFrequencyToChannel(new_frequency);
WiFiFrequencyRange range = WiFiChannelToFrequencyRange(channel);
WiFiFrequencyRange new_range = WiFiChannelToFrequencyRange(new_channel);
WiFiApChannelSwitch channel_switch = kWiFiApChannelSwitchUndef;
if (range == kWiFiFrequencyRange24 && new_range == kWiFiFrequencyRange24) {
channel_switch = kWiFiApChannelSwitch24To24;
} else if (range == kWiFiFrequencyRange24 &&
new_range == kWiFiFrequencyRange5) {
channel_switch = kWiFiApChannelSwitch24To5;
} else if (range == kWiFiFrequencyRange5 &&
new_range == kWiFiFrequencyRange24) {
channel_switch = kWiFiApChannelSwitch5To24;
} else if (range == kWiFiFrequencyRange5 &&
new_range == kWiFiFrequencyRange5) {
channel_switch = kWiFiApChannelSwitch5To5;
}
SendEnumToUMA(kMetricApChannelSwitch, channel_switch);
}
void Metrics::NotifyAp80211kSupport(bool neighbor_list_supported) {
SendBoolToUMA(kMetricAp80211kSupport, neighbor_list_supported);
}
void Metrics::NotifyAp80211rSupport(bool ota_ft_supported,
bool otds_ft_supported) {
WiFiAp80211rSupport support = kWiFiAp80211rNone;
if (otds_ft_supported) {
support = kWiFiAp80211rOTDS;
} else if (ota_ft_supported) {
support = kWiFiAp80211rOTA;
}
SendEnumToUMA(kMetricAp80211rSupport, support);
}
void Metrics::NotifyAp80211vDMSSupport(bool dms_supported) {
SendBoolToUMA(kMetricAp80211vDMSSupport, dms_supported);
}
void Metrics::NotifyAp80211vBSSMaxIdlePeriodSupport(
bool bss_max_idle_period_supported) {
SendBoolToUMA(kMetricAp80211vBSSMaxIdlePeriodSupport,
bss_max_idle_period_supported);
}
void Metrics::NotifyAp80211vBSSTransitionSupport(
bool bss_transition_supported) {
SendBoolToUMA(kMetricAp80211vBSSTransitionSupport, bss_transition_supported);
}
void Metrics::NotifyCiscoAdaptiveFTSupport(bool adaptive_ft_supported) {
SendBoolToUMA(kMetricCiscoAdaptiveFTSupport, adaptive_ft_supported);
}
void Metrics::NotifyANQPSupport(bool anqp_supported) {
SendBoolToUMA(kMetricAp80211uANQPSupport, anqp_supported);
}
void Metrics::Notify80211Disconnect(WiFiDisconnectByWhom by_whom,
IEEE_80211::WiFiReasonCode reason) {
EnumMetric<FixedName> metric_disconnect_reason;
EnumMetric<FixedName> metric_disconnect_type;
WiFiReasonType type;
if (by_whom == kDisconnectedByAp) {
metric_disconnect_reason = kMetricLinkApDisconnectReason;
metric_disconnect_type = kMetricLinkApDisconnectType;
type = kReasonCodeTypeByAp;
} else {
metric_disconnect_reason = kMetricLinkClientDisconnectReason;
metric_disconnect_type = kMetricLinkClientDisconnectType;
switch (reason) {
case IEEE_80211::kReasonCodeSenderHasLeft:
case IEEE_80211::kReasonCodeDisassociatedHasLeft:
type = kReasonCodeTypeByUser;
break;
case IEEE_80211::kReasonCodeInactivity:
type = kReasonCodeTypeConsideredDead;
break;
default:
type = kReasonCodeTypeByClient;
break;
}
}
SendEnumToUMA(metric_disconnect_reason, reason);
SendEnumToUMA(metric_disconnect_type, type);
}
void Metrics::RegisterDevice(int interface_index, Technology technology) {
SLOG(2) << __func__ << ": " << interface_index;
if (IsPrimaryConnectivityTechnology(technology)) {
bootstat::BootStat().LogEvent(
base::StringPrintf("network-%s-registered",
TechnologyName(technology).c_str())
.c_str());
}
auto device_metrics = std::make_unique<DeviceMetrics>();
device_metrics->technology = technology;
auto histogram =
GetFullMetricName(kMetricTimeToInitializeMillisecondsSuffix, technology);
device_metrics->initialization_timer.reset(
new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToInitializeMillisecondsMin,
kMetricTimeToInitializeMillisecondsMax,
kMetricTimeToInitializeMillisecondsNumBuckets));
device_metrics->initialization_timer->Start();
histogram =
GetFullMetricName(kMetricTimeToEnableMillisecondsSuffix, technology);
device_metrics->enable_timer.reset(new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToEnableMillisecondsMin,
kMetricTimeToEnableMillisecondsMax,
kMetricTimeToEnableMillisecondsNumBuckets));
histogram =
GetFullMetricName(kMetricTimeToDisableMillisecondsSuffix, technology);
device_metrics->disable_timer.reset(new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToDisableMillisecondsMin,
kMetricTimeToDisableMillisecondsMax,
kMetricTimeToDisableMillisecondsNumBuckets));
histogram =
GetFullMetricName(kMetricTimeToScanMillisecondsSuffix, technology);
device_metrics->scan_timer.reset(new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToScanMillisecondsMin,
kMetricTimeToScanMillisecondsMax,
kMetricTimeToScanMillisecondsNumBuckets));
histogram =
GetFullMetricName(kMetricTimeToConnectMillisecondsSuffix, technology);
device_metrics->connect_timer.reset(new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToConnectMillisecondsMin,
kMetricTimeToConnectMillisecondsMax,
kMetricTimeToConnectMillisecondsNumBuckets));
histogram = GetFullMetricName(kMetricTimeToScanAndConnectMillisecondsSuffix,
technology);
device_metrics->scan_connect_timer.reset(new chromeos_metrics::TimerReporter(
histogram, kMetricTimeToScanMillisecondsMin,
kMetricTimeToScanMillisecondsMax + kMetricTimeToConnectMillisecondsMax,
kMetricTimeToScanMillisecondsNumBuckets +
kMetricTimeToConnectMillisecondsNumBuckets));
devices_metrics_[interface_index] = std::move(device_metrics);
}
bool Metrics::IsDeviceRegistered(int interface_index, Technology technology) {
SLOG(2) << __func__ << ": interface index: " << interface_index
<< ", technology: " << technology;
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr)
return false;
// Make sure the device technologies match.
return (technology == device_metrics->technology);
}
void Metrics::DeregisterDevice(int interface_index) {
SLOG(2) << __func__ << ": interface index: " << interface_index;
devices_metrics_.erase(interface_index);
}
void Metrics::NotifyDeviceInitialized(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
if (!device_metrics->initialization_timer->Stop()) {
return;
}
ReportMilliseconds(*device_metrics->initialization_timer);
}
void Metrics::NotifyDeviceEnableStarted(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr)
return;
device_metrics->enable_timer->Start();
}
void Metrics::NotifyDeviceEnableFinished(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
if (!device_metrics->enable_timer->Stop()) {
return;
}
ReportMilliseconds(*device_metrics->enable_timer);
}
void Metrics::NotifyDeviceDisableStarted(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
device_metrics->disable_timer->Start();
}
void Metrics::NotifyDeviceDisableFinished(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
if (!device_metrics->disable_timer->Stop()) {
return;
}
ReportMilliseconds(*device_metrics->disable_timer);
}
void Metrics::NotifyDeviceScanStarted(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
device_metrics->scan_timer->Start();
device_metrics->scan_connect_timer->Start();
}
void Metrics::NotifyDeviceScanFinished(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
if (!device_metrics->scan_timer->Stop()) {
return;
}
// Don't send TimeToScan metrics if the elapsed time exceeds the max metrics
// value. Huge scan times usually mean something's gone awry; for cellular,
// for instance, this usually means that the modem is in an area without
// service and we're not interested in this scenario.
base::TimeDelta elapsed_time;
device_metrics->scan_timer->GetElapsedTime(&elapsed_time);
if (elapsed_time.InMilliseconds() <= kMetricTimeToScanMillisecondsMax) {
ReportMilliseconds(*device_metrics->scan_timer);
}
}
void Metrics::ReportDeviceScanResultToUma(Metrics::WiFiScanResult result) {
SendEnumToUMA(kMetricScanResult, result);
}
void Metrics::ResetScanTimer(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr)
return;
device_metrics->scan_timer->Reset();
}
void Metrics::NotifyDeviceConnectStarted(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr)
return;
device_metrics->connect_timer->Start();
}
void Metrics::NotifyDeviceConnectFinished(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
if (!device_metrics->connect_timer->Stop()) {
return;
}
ReportMilliseconds(*device_metrics->connect_timer);
if (!device_metrics->scan_connect_timer->Stop())
return;
ReportMilliseconds(*device_metrics->scan_connect_timer);
}
void Metrics::ResetConnectTimer(int interface_index) {
DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index);
if (device_metrics == nullptr) {
return;
}
device_metrics->connect_timer->Reset();
device_metrics->scan_connect_timer->Reset();
}
void Metrics::NotifyCellularDeviceDrop(const std::string& network_technology,
uint16_t signal_strength) {
SLOG(2) << __func__ << ": " << network_technology << ", " << signal_strength;
CellularDropTechnology drop_technology = kCellularDropTechnologyUnknown;
if (network_technology == kNetworkTechnology1Xrtt) {
drop_technology = kCellularDropTechnology1Xrtt;
} else if (network_technology == kNetworkTechnologyEdge) {
drop_technology = kCellularDropTechnologyEdge;
} else if (network_technology == kNetworkTechnologyEvdo) {
drop_technology = kCellularDropTechnologyEvdo;
} else if (network_technology == kNetworkTechnologyGprs) {
drop_technology = kCellularDropTechnologyGprs;
} else if (network_technology == kNetworkTechnologyGsm) {
drop_technology = kCellularDropTechnologyGsm;
} else if (network_technology == kNetworkTechnologyHspa) {
drop_technology = kCellularDropTechnologyHspa;
} else if (network_technology == kNetworkTechnologyHspaPlus) {
drop_technology = kCellularDropTechnologyHspaPlus;
} else if (network_technology == kNetworkTechnologyLte) {
drop_technology = kCellularDropTechnologyLte;
} else if (network_technology == kNetworkTechnologyUmts) {
drop_technology = kCellularDropTechnologyUmts;
} else if (network_technology == kNetworkTechnology5gNr) {
drop_technology = kCellularDropTechnology5gNr;
}
SendEnumToUMA(kMetricCellularDrop, drop_technology);
SendToUMA(kMetricCellularSignalStrengthBeforeDrop, signal_strength);
}
void Metrics::NotifyCellularConnectionResult(
Error::Type error, DetailedCellularConnectionResult::APNType apn_type) {
SLOG(2) << __func__ << ": " << error;
DCHECK(apn_type != DetailedCellularConnectionResult::APNType::kAttach)
<< "shill should not send this metric for Attach APNs";
CellularConnectResult connect_result =
ConvertErrorToCellularConnectResult(error);
SendEnumToUMA(kMetricCellularConnectResult, apn_type,
static_cast<int>(connect_result));
}
int64_t Metrics::HashApn(const std::string& uuid,
const std::string& apn_name,
const std::string& username,
const std::string& password) {
std::string string1, string2;
base::TrimString(uuid, " ", &string1);
base::TrimString(apn_name, " ", &string2);
string1 += string2;
base::TrimString(username, " ", &string2);
string1 += string2;
base::TrimString(password, " ", &string2);
string1 += string2;
int64_t hash;
crypto::SHA256HashString(string1, &hash, 8);
return hash;
}
std::optional<int64_t> Metrics::IntGid1(const std::string& gid1) {
// Ignore if GID1 not populated in the SIM card
if (gid1.empty())
return std::nullopt;
// GID1 has no predefined max length defined, so limit it ourselves
// * Input string is in HEX (so 2 chars per byte).
// * Limit the input string to 8 bytes in order to fit it in a
// 64bit integer value.
// * The most usual cases are 0, 1 or 2 bytes,
int64_t parsed;
if (!base::HexStringToInt64(gid1.substr(0, 2 * (sizeof(int64_t)) - 1),
&parsed)) {
LOG(ERROR) << "Failed to parse GID1 as an integer: " << gid1;
return std::nullopt;
}
return parsed;
}
void Metrics::NotifyCellularNetworkValidationResult(
const CellularNetworkValidationResult& result) {
int64_t home, serving;
std::string apn_name;
std::string username;
std::string password;
CellularRoamingState roaming =
CellularRoamingState::kCellularRoamingStateUnknown;
base::StringToInt64(result.home_mccmnc, &home);
base::StringToInt64(result.serving_mccmnc, &serving);
if (result.roaming_state == kRoamingStateHome) {
roaming = kCellularRoamingStateHome;
} else if (result.roaming_state == kRoamingStateRoaming) {
roaming = kCellularRoamingStateRoaming;
}
DCHECK(base::Contains(result.apn_info, kApnSourceProperty));
if (base::Contains(result.apn_info, kApnSourceProperty)) {
if (result.apn_info.at(kApnSourceProperty) == kApnSourceMoDb ||
result.apn_info.at(kApnSourceProperty) == kApnSourceModem) {
if (base::Contains(result.apn_info, kApnProperty))
apn_name = result.apn_info.at(kApnProperty);
if (base::Contains(result.apn_info, kApnUsernameProperty))
username = result.apn_info.at(kApnUsernameProperty);
if (base::Contains(result.apn_info, kApnPasswordProperty))
password = result.apn_info.at(kApnPasswordProperty);
}
}
LOG(INFO) << __func__
<< ": portal_detection_result: " << result.portal_detection_result
<< " portal_detection_count: " << result.portal_detection_count
<< " apn:" << apn_name
<< " ipv4:" << static_cast<int>(result.ipv4_config_method)
<< " ipv6:" << static_cast<int>(result.ipv6_config_method)
<< " home_mccmnc:" << result.home_mccmnc
<< " serving_mccmnc:" << result.serving_mccmnc
<< " roaming_state:" << result.roaming_state
<< " tech_used:" << result.tech_used
<< " sim_type:" << static_cast<int>(result.sim_type);
auto event =
metrics::structured::events::cellular::CellularNetworkValidationAttempt()
.Setportal_detection_result(
static_cast<int64_t>(result.portal_detection_result))
.Setinitial_result(
static_cast<int64_t>(result.portal_detection_count == 1 ? 1 : 0))
.Setapn_id(HashApn(result.uuid, apn_name, username, password))
.Setipv4_config_method(static_cast<int>(result.ipv4_config_method))
.Setipv6_config_method(static_cast<int>(result.ipv6_config_method))
.Sethome_mccmnc(home)
.Setserving_mccmnc(serving)
.Setroaming_state(roaming)
.Settech_used(result.tech_used)
.Setsim_type(static_cast<int>(result.sim_type));
event.Record();
}
void Metrics::NotifyDetailedCellularConnectionResult(
const DetailedCellularConnectionResult& result) {
int64_t home, serving, detailed_error_hash;
CellularApnSource apn_source = kCellularApnSourceUi;
std::string apn_name;
std::string username;
std::string password;
CellularRoamingState roaming =
CellularRoamingState::kCellularRoamingStateUnknown;
CellularConnectResult connect_result =
ConvertErrorToCellularConnectResult(result.error);
uint32_t connect_time = 0;
uint32_t scan_connect_time = 0;
DeviceMetrics* device_metrics = GetDeviceMetrics(result.interface_index);
base::StringToInt64(result.home_mccmnc, &home);
base::StringToInt64(result.serving_mccmnc, &serving);
crypto::SHA256HashString(result.detailed_error, &detailed_error_hash, 8);
if (result.roaming_state == kRoamingStateHome)
roaming = kCellularRoamingStateHome;
else if (result.roaming_state == kRoamingStateRoaming)
roaming = kCellularRoamingStateRoaming;
DCHECK(base::Contains(result.apn_info, kApnSourceProperty));
if (base::Contains(result.apn_info, kApnSourceProperty)) {
if (result.apn_info.at(kApnSourceProperty) == kApnSourceMoDb)
apn_source = kCellularApnSourceMoDb;
else if (result.apn_info.at(kApnSourceProperty) == kApnSourceUi)
apn_source = kCellularApnSourceUi;
else if (result.apn_info.at(kApnSourceProperty) == kApnSourceModem)
apn_source = kCellularApnSourceModem;
else if (result.apn_info.at(kApnSourceProperty) == kApnSourceAdmin)
apn_source = kCellularApnSourceAdmin;
else if (result.apn_info.at(kApnSourceProperty) ==
cellular::kApnSourceFallback)
apn_source = kCellularApnSourceFallback;
if (result.apn_info.at(kApnSourceProperty) == kApnSourceMoDb ||
result.apn_info.at(kApnSourceProperty) == kApnSourceModem) {
if (base::Contains(result.apn_info, kApnProperty))
apn_name = result.apn_info.at(kApnProperty);
if (base::Contains(result.apn_info, kApnUsernameProperty))
username = result.apn_info.at(kApnUsernameProperty);
if (base::Contains(result.apn_info, kApnPasswordProperty))
password = result.apn_info.at(kApnPasswordProperty);
}
}
// apn_types is represented by a bit mask.
uint32_t apn_types = 0;
if (ApnList::IsDefaultApn(result.apn_info)) {
apn_types |= static_cast<uint32_t>(
Metrics::CellularApnType::kCellularApnTypeDefault);
}
if (ApnList::IsAttachApn(result.apn_info)) {
apn_types |= static_cast<uint32_t>(CellularApnType::kCellularApnTypeIA);
}
if (ApnList::IsTetheringApn(result.apn_info)) {
apn_types |= static_cast<uint32_t>(CellularApnType::kCellularApnTypeDun);
}
// Each APN type in connection_apn_types is represented by a digit, and the
// order of the digits represent the connection order from first on the left,
// to last on the right. For example, a value of 23 indicates that a Default
// connection exists, and a new connection attempt is tried with a DUN APN.
uint32_t connection_apn_types = 0;
for (auto& apn_type : result.connection_apn_types) {
uint digit = 0;
switch (apn_type) {
case DetailedCellularConnectionResult::APNType::kAttach:
digit = 1;
break;
case DetailedCellularConnectionResult::APNType::kDefault:
digit = 2;
break;
case DetailedCellularConnectionResult::APNType::kDUN:
digit = 3;
break;
}
connection_apn_types = connection_apn_types * 10 + digit;
}
if (device_metrics != nullptr) {
base::TimeDelta elapsed_time;
device_metrics->connect_timer->GetElapsedTime(&elapsed_time);
connect_time = elapsed_time.InMilliseconds();
device_metrics->scan_connect_timer->GetElapsedTime(&elapsed_time);
scan_connect_time = elapsed_time.InMilliseconds();
}
SLOG(3) << __func__ << ": error:" << result.error << " uuid:" << result.uuid
<< " apn:" << apn_name << " apn_source:" << apn_source
<< " use_apn_revamp_ui: " << result.use_apn_revamp_ui
<< " apn_types: " << apn_types
<< " connection_apn_types: " << connection_apn_types
<< " ipv4:" << static_cast<int>(result.ipv4_config_method)
<< " ipv6:" << static_cast<int>(result.ipv6_config_method)
<< " home_mccmnc:" << result.home_mccmnc
<< " serving_mccmnc:" << result.serving_mccmnc
<< " roaming_state:" << result.roaming_state
<< " tech_used:" << result.tech_used
<< " iccid_length:" << result.iccid_length
<< " sim_type:" << static_cast<int>(result.sim_type)
<< " gid1:" << result.gid1 << " modem_state:" << result.modem_state
<< " connect_time:" << connect_time
<< " scan_connect_time:" << scan_connect_time
<< " detailed_error:" << result.detailed_error
<< " connection_attempt_type:"
<< static_cast<int>(result.connection_attempt_type)
<< " subscription_error_seen: " << result.subscription_error_seen
<< " last_connected: " << result.last_connected
<< " last_online: " << result.last_online;
auto event =
metrics::structured::events::cellular::CellularConnectionAttempt()
.Setconnect_result(static_cast<int64_t>(connect_result))
.Setapn_id(HashApn(result.uuid, apn_name, username, password))
.Setipv4_config_method(static_cast<int>(result.ipv4_config_method))
.Setipv6_config_method(static_cast<int>(result.ipv6_config_method))
.Sethome_mccmnc(home)
.Setserving_mccmnc(serving)
.Setroaming_state(roaming)
.Setapn_types(apn_types)
.Setapn_source(static_cast<int64_t>(apn_source))
.Settech_used(result.tech_used)
.Seticcid_length(result.iccid_length)
.Setsim_type(static_cast<int>(result.sim_type))
.Setmodem_state(result.modem_state)
.Setconnect_time(connect_time)
.Setscan_connect_time(scan_connect_time)
.Setdetailed_error(detailed_error_hash)
.Setuse_apn_revamp_ui(result.use_apn_revamp_ui)
.Setconnection_attempt_type(
static_cast<int>(result.connection_attempt_type))
.Setsubscription_error_seen(result.subscription_error_seen)
.Setconnection_apn_types(connection_apn_types)
.Setlast_connected(result.last_connected)
.Setlast_online(result.last_online);
std::optional<int64_t> gid1 = IntGid1(result.gid1);
if (gid1.has_value()) {
event.Setgid1(gid1.value());
}
event.Record();
}
void Metrics::NotifyCellularPowerOptimization(
const CellularPowerOptimizationInfo& power_opt_info) {
LOG(INFO) << __func__ << ": power optimization reason: "
<< static_cast<int>(power_opt_info.reason);
metrics::structured::events::cellular::PowerOptimization()
.Setpower_state(static_cast<int>(power_opt_info.new_power_state))
.Setreason(static_cast<int>(power_opt_info.reason))
.Setsince_last_online_hours(power_opt_info.since_last_online_hours)
.Record();
}
void Metrics::NotifyCellularEntitlementCheckResult(
Metrics::CellularEntitlementCheck result) {
SendEnumToUMA(kMetricCellularEntitlementCheck, result);
}
bool Metrics::SendEnumToUMA(const std::string& name, int sample, int max) {
SLOG(5) << "Sending enum " << name << " with value " << sample << ".";
return library_->SendEnumToUMA(name, sample, max);
}
bool Metrics::SendBoolToUMA(const std::string& name, bool b) {
SLOG(5) << "Sending bool " << name << " with value " << b << ".";
return library_->SendBoolToUMA(name, b);
}
bool Metrics::SendToUMA(
const std::string& name, int sample, int min, int max, int num_buckets) {
SLOG(5) << "Sending metric " << name << " with value " << sample << ".";
return library_->SendToUMA(name, sample, min, max, num_buckets);
}
bool Metrics::SendSparseToUMA(const std::string& name, int sample) {
SLOG(5) << "Sending sparse metric " << name << " with value " << sample
<< ".";
return library_->SendSparseToUMA(name, sample);
}
void Metrics::ReportMilliseconds(const chromeos_metrics::TimerReporter& timer) {
base::TimeDelta elapsed_time;
if (timer.GetElapsedTime(&elapsed_time)) {
SendToUMA(timer.histogram_name(), elapsed_time.InMilliseconds(),
timer.min(), timer.max(), timer.num_buckets());
}
}
void Metrics::NotifyConnectionDiagnosticsIssue(const std::string& issue) {
ConnectionDiagnosticsIssue issue_enum;
if (issue == ConnectionDiagnostics::kIssueIPCollision) {
issue_enum = kConnectionDiagnosticsIssueIPCollision;
} else if (issue == ConnectionDiagnostics::kIssueRouting) {
issue_enum = kConnectionDiagnosticsIssueRouting;
} else if (issue == ConnectionDiagnostics::kIssueHTTP) {
issue_enum = kConnectionDiagnosticsIssueHTTP;
} else if (issue == ConnectionDiagnostics::kIssueDNSServerMisconfig) {
issue_enum = kConnectionDiagnosticsIssueDNSServerMisconfig;
} else if (issue == ConnectionDiagnostics::kIssueDNSServerNoResponse) {
issue_enum = kConnectionDiagnosticsIssueDNSServerNoResponse;
} else if (issue == ConnectionDiagnostics::kIssueNoDNSServersConfigured) {
issue_enum = kConnectionDiagnosticsIssueNoDNSServersConfigured;
} else if (issue == ConnectionDiagnostics::kIssueDNSServersInvalid) {
issue_enum = kConnectionDiagnosticsIssueDNSServersInvalid;
} else if (issue == ConnectionDiagnostics::kIssueNone) {
issue_enum = kConnectionDiagnosticsIssueNone;
} else if (issue == ConnectionDiagnostics::kIssueGatewayUpstream) {
issue_enum = kConnectionDiagnosticsIssueGatewayUpstream;
} else if (issue == ConnectionDiagnostics::kIssueGatewayNotResponding) {
issue_enum = kConnectionDiagnosticsIssueGatewayNotResponding;
} else if (issue == ConnectionDiagnostics::kIssueServerNotResponding) {
issue_enum = kConnectionDiagnosticsIssueServerNotResponding;
} else if (issue == ConnectionDiagnostics::kIssueGatewayArpFailed) {
issue_enum = kConnectionDiagnosticsIssueGatewayArpFailed;
} else if (issue == ConnectionDiagnostics::kIssueServerArpFailed) {
issue_enum = kConnectionDiagnosticsIssueServerArpFailed;
} else if (issue == ConnectionDiagnostics::kIssueInternalError) {
issue_enum = kConnectionDiagnosticsIssueInternalError;
} else if (issue == ConnectionDiagnostics::kIssueGatewayNoNeighborEntry) {
issue_enum = kConnectionDiagnosticsIssueGatewayNoNeighborEntry;
} else if (issue == ConnectionDiagnostics::kIssueServerNoNeighborEntry) {
issue_enum = kConnectionDiagnosticsIssueServerNoNeighborEntry;
} else if (issue ==
ConnectionDiagnostics::kIssueGatewayNeighborEntryNotConnected) {
issue_enum = kConnectionDiagnosticsIssueGatewayNeighborEntryNotConnected;
} else if (issue ==
ConnectionDiagnostics::kIssueServerNeighborEntryNotConnected) {
issue_enum = kConnectionDiagnosticsIssueServerNeighborEntryNotConnected;
} else {
LOG(ERROR) << __func__ << ": Invalid issue: " << issue;
return;
}
SendEnumToUMA(kMetricConnectionDiagnosticsIssue, issue_enum);
}
void Metrics::NotifyHS20Support(bool hs20_supported, int hs20_version_number) {
if (!hs20_supported) {
SendEnumToUMA(kMetricHS20Support, kHS20Unsupported);
return;
}
int hotspot_version = kHS20VersionInvalid;
switch (hs20_version_number) {
// Valid values.
case 1:
hotspot_version = kHS20Version1;
break;
case 2:
hotspot_version = kHS20Version2;
break;
case 3:
hotspot_version = kHS20Version3;
break;
// Invalid values.
default:
break;
}
SendEnumToUMA(kMetricHS20Support, hotspot_version);
}
void Metrics::NotifyMBOSupport(bool mbo_support) {
SendBoolToUMA(kMetricMBOSupport, mbo_support);
}
void Metrics::NotifyStreamClassificationSupport(bool scs_supported,
bool mscs_supported) {
int sc_support = kWiFiApSCUnsupported;
if (scs_supported && mscs_supported) {
sc_support = kWiFiApSCBoth;
} else if (scs_supported) {
sc_support = kWiFiApSCS;
} else if (mscs_supported) {
sc_support = kWiFiApMSCS;
}
SendEnumToUMA(kMetricApSCSupport, sc_support);
}
void Metrics::NotifyAlternateEDCASupport(bool alternate_edca_supported) {
SendBoolToUMA(kMetricApAlternateEDCASupport, alternate_edca_supported);
}
void Metrics::NotifyWiFiConnectionUnreliable() {
// Report the results of the metric associated with tracking the
// time between rekey and unreliable connection,
// TimeFromRekeyToFailureSeconds.
auto& rekey_timer = time_between_rekey_and_connection_failure_timer_;
base::TimeDelta elapsed;
int seconds;
if (!rekey_timer->HasStarted()) {
return;
}
rekey_timer->GetElapsedTime(&elapsed);
seconds = elapsed.InSeconds();
if (seconds < kMetricTimeFromRekeyToFailureSeconds.max) {
// We only send the metric if the unreliable connection happens shortly
// after the rekey started on the same BSSID.
LOG(INFO) << "Connection became unreliable shortly after rekey, "
<< "seconds between rekey and connection failure: " << seconds;
SendToUMA(kMetricTimeFromRekeyToFailureSeconds, seconds);
}
rekey_timer->Reset();
}
void Metrics::NotifyBSSIDChanged() {
// Rekey cancelled/BSSID changed, so we reset the timer
// associated with the metric for TimeFromRekeyToFailureSeconds.
time_between_rekey_and_connection_failure_timer_->Reset();
}
void Metrics::NotifyRekeyStart() {
// Start the timer associated with the metric tracking time
// between rekey and unreliable connection,
// TimeFromRekeyToFailureSeconds.
auto& rekey_timer = time_between_rekey_and_connection_failure_timer_;
if (!rekey_timer->HasStarted()) {
rekey_timer->Start();
}
}
void Metrics::NotifyWiFiBadPassphrase(bool ever_connected, bool user_initiate) {
WiFiBadPassphraseServiceType type;
if (user_initiate) {
type = ever_connected ? kUserInitiatedConnectedBefore
: kUserInitiatedNeverConnected;
} else {
type = ever_connected ? kNonUserInitiatedConnectedBefore
: kNonUserInitiatedNeverConnected;
}
SendEnumToUMA(kMetricWiFiBadPassphraseServiceType, type);
}
void Metrics::NotifyWiFiAdapterStateChanged(bool enabled,
const WiFiAdapterInfo& info) {
metrics::structured::events::wi_fi_chipset::WiFiChipsetInfo()
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetVendorId(info.vendor_id)
.SetProductId(info.product_id)
.SetSubsystemId(info.subsystem_id)
.Record();
bool adapter_supported = WiFiMetricsUtils::CanReportAdapterInfo(info);
if (enabled) {
// Monitor through UMA how often adapters are not in the allowlist.
WiFiAdapterInAllowlist allowed =
adapter_supported ? kInAVL : kNotInAllowlist;
SendEnumToUMA(kMetricAdapterInfoAllowlisted, allowed);
}
int v_id = adapter_supported ? info.vendor_id
: Metrics::kWiFiStructuredMetricsErrorValue;
int p_id = adapter_supported ? info.product_id
: Metrics::kWiFiStructuredMetricsErrorValue;
int s_id = adapter_supported ? info.subsystem_id
: Metrics::kWiFiStructuredMetricsErrorValue;
metrics::structured::events::wi_fi::WiFiAdapterStateChanged()
.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetAdapterState(enabled)
.SetVendorId(v_id)
.SetProductId(p_id)
.SetSubsystemId(s_id)
.Record();
}
void Metrics::NotifyWiFiConnectionAttempt(const WiFiConnectionAttemptInfo& info,
uint64_t session_tag) {
metrics::structured::events::wi_fi_ap::WiFiAPInfo()
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetAPOUI(info.ap_oui)
.Record();
int oui = shill::WiFiMetricsUtils::CanReportOUI(info.ap_oui) ? info.ap_oui
: 0xFFFFFFFF;
// Do NOT modify the verbosity of the Session Tag log without a privacy
// review.
SLOG(WiFiService::kSessionTagMinimumLogVerbosity)
<< __func__ << ": Session Tag 0x" << PseudonymizeTag(session_tag);
metrics::structured::events::wi_fi::WiFiConnectionAttempt()
.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetSessionTag(session_tag)
.SetAttemptType(info.type)
.SetAPPhyMode(info.mode)
.SetAPSecurityMode(info.security)
.SetAPSecurityEAPInnerProtocol(info.eap_inner)
.SetAPSecurityEAPOuterProtocol(info.eap_outer)
.SetAPBand(info.band)
.SetAPChannel(info.channel)
.SetRSSI(info.rssi)
.SetSSID(info.ssid)
.SetSSIDProvisioningMode(info.provisioning_mode)
.SetSSIDHidden(info.ssid_hidden)
.SetBSSID(info.bssid.has_value() ? info.bssid->ToString() : "")
.SetAPOUI(oui)
.SetAP_80211krv_NLSSupport(
info.ap_features.krv_info.neighbor_list_supported)
.SetAP_80211krv_OTA_FTSupport(info.ap_features.krv_info.ota_ft_supported)
.SetAP_80211krv_OTDS_FTSupport(
info.ap_features.krv_info.otds_ft_supported)
.SetAP_80211krv_DMSSupport(info.ap_features.krv_info.dms_supported)
.SetAP_80211krv_BSSMaxIdleSupport(
info.ap_features.krv_info.bss_max_idle_period_supported)
.SetAP_80211krv_BSSTMSupport(
info.ap_features.krv_info.bss_transition_supported)
.SetAP_HS20Support(info.ap_features.hs20_info.supported)
.SetAP_HS20Version(info.ap_features.hs20_info.version)
.SetAP_MBOSupport(info.ap_features.mbo_supported)
.Record();
}
void Metrics::NotifyWiFiConnectionAttemptResult(
Metrics::NetworkServiceError result_code, uint64_t session_tag) {
// Do NOT modify the verbosity of the Session Tag log without a privacy
// review.
SLOG(WiFiService::kSessionTagMinimumLogVerbosity)
<< __func__ << ": Session Tag 0x" << PseudonymizeTag(session_tag);
SLOG(2) << __func__ << ": ResultCode " << result_code;
metrics::structured::events::wi_fi::WiFiConnectionAttemptResult()
.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetSessionTag(session_tag)
.SetResultCode(result_code)
.Record();
}
void Metrics::NotifyWiFiDisconnection(WiFiDisconnectionType type,
IEEE_80211::WiFiReasonCode reason,
uint64_t session_tag) {
// Do NOT modify the verbosity of the Session Tag log without a privacy
// review.
SLOG(WiFiService::kSessionTagMinimumLogVerbosity)
<< __func__ << ": Session Tag 0x" << PseudonymizeTag(session_tag);
SLOG(2) << __func__ << ": Type " << type << " Reason " << reason;
metrics::structured::events::wi_fi::WiFiConnectionEnd()
.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetSessionTag(session_tag)
.SetDisconnectionType(type)
.SetDisconnectionReasonCode(reason)
.Record();
}
void Metrics::NotifyWiFiLinkQualityTrigger(WiFiLinkQualityTrigger trigger,
uint64_t session_tag) {
// Do NOT modify the verbosity of the Session Tag log without a privacy
// review.
SLOG(WiFiService::kSessionTagMinimumLogVerbosity)
<< __func__ << ": Session Tag 0x" << PseudonymizeTag(session_tag);
SLOG(2) << __func__ << ": Trigger " << trigger;
metrics::structured::events::wi_fi::WiFiLinkQualityTrigger()
.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetSessionTag(session_tag)
.SetType(trigger)
.Record();
}
void Metrics::NotifyWiFiLinkQualityReport(const WiFiLinkQualityReport& report,
uint64_t session_tag) {
// Do NOT modify the verbosity of the Session Tag log without a privacy
// review.
SLOG(WiFiService::kSessionTagMinimumLogVerbosity)
<< __func__ << ": Session Tag 0x" << PseudonymizeTag(session_tag);
// Note: RXChannelWidth and TXChannelWidth have identical values but we have
// 2 separate fields for backward compatibility reasons.
metrics::structured::events::wi_fi::WiFiLinkQualityReport sm_report =
metrics::structured::events::wi_fi::WiFiLinkQualityReport();
sm_report.SetBootId(WiFiMetricsUtils::GetBootId())
.SetSystemTime(GetMicroSecondsMonotonic())
.SetEventVersion(kWiFiStructuredMetricsVersion)
.SetSessionTag(session_tag)
.SetRXPackets(report.rx.packets)
.SetRXBytes(report.rx.bytes)
.SetTXPackets(report.tx.packets)
.SetTXBytes(report.tx.bytes)
.SetTXRetries(report.tx_retries)
.SetTXFailures(report.tx_failures)
.SetRXDrops(report.rx_drops)
.SetChain0Signal(report.chain0_signal)
.SetChain0SignalAvg(report.chain0_signal_avg)
.SetChain1Signal(report.chain1_signal)
.SetChain1SignalAvg(report.chain1_signal_avg)
.SetBeaconSignalAvg(report.beacon_signal_avg)
.SetBeaconsReceived(report.beacons_received)
.SetBeaconsLost(report.beacons_lost)
.SetExpectedThroughput(report.expected_throughput)
.SetRXRate(report.rx.bitrate)
.SetRXMCS(report.rx.mcs)
.SetRXChannelWidth(report.width)
.SetRXMode(report.rx.mode)
.SetRXGuardInterval(report.rx.gi)
.SetRXNSS(report.rx.nss)
.SetRXDCM(report.rx.dcm)
.SetTXRate(report.tx.bitrate)
.SetTXMCS(report.tx.mcs)
.SetTXChannelWidth(report.width)
.SetTXMode(report.tx.mode)
.SetTXGuardInterval(report.tx.gi)
.SetTXNSS(report.tx.nss)
.SetTXDCM(report.tx.dcm)
.SetFCSErrors(report.fcs_errors)
.SetRXMPDUS(report.rx_mpdus)
.SetInactiveTime(report.inactive_time)
.SetNoise(report.noise)
.SetAckSignalAverage(report.ack_signal_avg)
.SetLastAckSignal(report.last_ack_signal)
.SetSignal(report.signal)
.SetSignalAverage(report.signal_avg);
#if !defined(DISABLE_FLOSS)
sm_report.SetBTEnabled(report.bt_enabled)
.SetBTStack(report.bt_stack)
.SetBTHFP(report.bt_hfp)
.SetBTA2DP(report.bt_a2dp)
.SetBTActivelyScanning(report.bt_active_scanning);
#else // DISABLE_FLOSS
sm_report.SetBTStack(kBTStackBlueZ);
#endif // DISABLE_FLOSS
sm_report.Record();
}
// static
int Metrics::GetRegulatoryDomainValue(std::string country_code) {
// Convert country code to upper case before checking validity.
country_code = base::ToUpperASCII(country_code);
// Check if alpha2 attribute is a valid ISO / IEC 3166 alpha2 country code.
// "00", "99", "98" and "97" are special codes defined in
// linux/include/net/regulatory.h.
// According to https://www.iso.org/glossary-for-iso-3166.html, a subdivision
// code is based on the two-letter code element from ISO 3166-1 followed by
// a separator and up to three alphanumeric characters. ath10k uses '#' as
// the separator, as reported in b/217761687. New separators may be added
// if shown in reports. Currently, these country codes are valid:
// 1. Special code: 00, 99, 98, 97
// 2. Two-letter alpha 2 code, such as "US", "FR"
// 3. Subdivision code, two-letter alpha 2 code + '#' + up to three
// alphanumeric characters, such as "US#001", "JM#001", while the characters
// after '#' are ignored
if (country_code == "00") {
return kRegDom00;
} else if (country_code == "97") {
return kRegDom97;
} else if (country_code == "98") {
return kRegDom98;
} else if (country_code == "99") {
return kRegDom99;
} else if (country_code.length() < 2 || !std::isupper(country_code[0]) ||
!std::isupper(country_code[1]) || country_code.length() > 6 ||
(country_code.length() > 2 && country_code[2] != '#')) {
return kCountryCodeInvalid;
} else {
// Calculate corresponding country code value for UMA histogram.
return ((static_cast<int>(country_code[0]) - static_cast<int>('A')) * 26) +
(static_cast<int>(country_code[1]) - static_cast<int>('A') + 2);
}
}
Metrics::DeviceMetrics* Metrics::GetDeviceMetrics(int interface_index) const {
DeviceMetricsLookupMap::const_iterator it =
devices_metrics_.find(interface_index);
if (it == devices_metrics_.end()) {
SLOG(2) << __func__ << ": device " << interface_index << " not found";
return nullptr;
}
return it->second.get();
}
std::string Metrics::PseudonymizeTag(uint64_t tag) {
if (pseudo_tag_salt_.empty()) {
return "INVALID SALT";
}
if (IsInvalidTag(tag)) {
return "INVALID TAG";
}
uint8_t hash[kPseudoTagHashLen];
std::string salted_tag =
base::StrCat({pseudo_tag_salt_, base::NumberToString(tag)});
crypto::SHA256HashString(salted_tag, hash, std::size(hash));
return base::HexEncode(base::span<uint8_t>(hash, std::size(hash)));
}
void Metrics::SetLibraryForTesting(MetricsLibraryInterface* library) {
library_ = library;
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::EnumMetric<Metrics::FixedName>& metric) {
return stream << "EnumMetric<FixedName>{.n.name=" << metric.n.name
<< ", .max=" << metric.max << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::EnumMetric<Metrics::NameByApnType>& metric) {
return stream << "EnumMetric<NameByApnType>{.n.name=" << metric.n.name
<< ", .max=" << metric.max << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::EnumMetric<Metrics::NameByTechnology>& metric) {
return stream << "EnumMetric<NameByTechnology>{.n.name=" << metric.n.name
<< ", .max=" << metric.max << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::EnumMetric<Metrics::NameByVPNType>& metric) {
return stream << "EnumMetric<NameByVPNType>{.n.name=" << metric.n.name
<< ", .max=" << metric.max << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::EnumMetric<Metrics::PrefixName>& metric) {
return stream << "EnumMetric<PrefixName>{.n.prefix=" << metric.n.prefix
<< ", .max=" << metric.max << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::HistogramMetric<Metrics::FixedName>& metric) {
return stream << "HistogramMetric<FixedName>{.n.name=" << metric.n.name
<< ", .min=" << metric.min << ", .max=" << metric.max
<< ", num_buckets=" << metric.num_buckets << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::HistogramMetric<Metrics::NameByTechnology>& metric) {
return stream << "HistogramMetric<NameByTechnology>{.n.name=" << metric.n.name
<< ", .min=" << metric.min << ", .max=" << metric.max
<< ", num_buckets=" << metric.num_buckets << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::HistogramMetric<Metrics::PrefixName>& metric) {
return stream << "HistogramMetric<PrefixName>{.n.prefix=" << metric.n.prefix
<< ", .min=" << metric.min << ", .max=" << metric.max
<< ", num_buckets=" << metric.num_buckets << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::SparseMetric<Metrics::FixedName>& metric) {
return stream << "SparseMetric<FixedName>{.n.name=" << metric.n.name << "}";
}
std::ostream& operator<<(
std::ostream& stream,
const Metrics::SparseMetric<Metrics::NameByTechnology>& metric) {
return stream << "SparseMetric<NameByTechnology>{.n.name=" << metric.n.name
<< "}";
}
} // namespace shill