| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // 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 <iterator> |
| #include <memory> |
| #include <utility> |
| |
| #include <base/check.h> |
| #include <base/containers/contains.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.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/structured_events.h> |
| |
| #include "shill/cellular/cellular.h" |
| #include "shill/cellular/cellular_consts.h" |
| #include "shill/connection_diagnostics.h" |
| #include "shill/logging.h" |
| #include "shill/wifi/wifi_endpoint.h" |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kMetrics; |
| static std::string ObjectID(const Metrics* m) { |
| return "(metrics)"; |
| } |
| } // namespace Logging |
| |
| namespace { |
| |
| constexpr char kMetricPrefix[] = "Network.Shill"; |
| |
| 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; |
| default: |
| LOG(WARNING) << "Unexpected error type: " << error; |
| return Metrics::CellularConnectResult::kCellularConnectResultUnknown; |
| } |
| } |
| } // namespace |
| |
| Metrics::Metrics() |
| : library_(&metrics_library_), |
| last_default_technology_(Technology::kUnknown), |
| was_last_online_(false), |
| time_online_timer_(new chromeos_metrics::Timer), |
| time_to_drop_timer_(new chromeos_metrics::Timer), |
| time_resume_to_ready_timer_(new chromeos_metrics::Timer), |
| time_suspend_actions_timer(new chromeos_metrics::Timer), |
| time_(Time::GetInstance()) { |
| chromeos_metrics::TimerReporter::set_metrics_lib(library_); |
| } |
| |
| Metrics::~Metrics() = default; |
| |
| // 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(nullptr, 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::WiFiSecurity Metrics::WiFiSecurityStringToEnum( |
| const std::string& security) { |
| if (security == kSecurityNone) { |
| return kWiFiSecurityNone; |
| } else if (security == kSecurityWep) { |
| return kWiFiSecurityWep; |
| } else if (security == kSecurityWpa) { |
| return kWiFiSecurityWpa; |
| } else if (security == kSecurityRsn) { |
| return kWiFiSecurityRsn; |
| } else if (security == kSecurity8021x) { |
| return kWiFiSecurity8021x; |
| } else if (security == kSecurityPsk) { |
| return kWiFiSecurityPsk; |
| } else if (security == kSecurityWpa3) { |
| return kWiFiSecurityWpa3; |
| } else { |
| return kWiFiSecurityUnknown; |
| } |
| } |
| |
| // 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 |
| Metrics::PortalResult Metrics::PortalDetectionResultToEnum( |
| const PortalDetector::Result& portal_result) { |
| PortalResult retval = kPortalResultUnknown; |
| // The only time we should end a successful portal detection is when we're |
| // in the Content phase. If we end with kStatusSuccess in any other phase, |
| // then this indicates that something bad has happened. |
| switch (portal_result.http_phase) { |
| case PortalDetector::Phase::kDNS: |
| if (portal_result.http_status == PortalDetector::Status::kFailure) |
| retval = kPortalResultDNSFailure; |
| else if (portal_result.http_status == PortalDetector::Status::kTimeout) |
| retval = kPortalResultDNSTimeout; |
| else |
| LOG(DFATAL) << __func__ << ": Final result status " |
| << static_cast<int>(portal_result.http_status) |
| << " is not allowed in the DNS phase"; |
| break; |
| |
| case PortalDetector::Phase::kConnection: |
| if (portal_result.http_status == PortalDetector::Status::kFailure) |
| retval = kPortalResultConnectionFailure; |
| else if (portal_result.http_status == PortalDetector::Status::kTimeout) |
| retval = kPortalResultConnectionTimeout; |
| else |
| LOG(DFATAL) << __func__ << ": Final result status " |
| << static_cast<int>(portal_result.http_status) |
| << " is not allowed in the Connection phase"; |
| break; |
| |
| case PortalDetector::Phase::kHTTP: |
| if (portal_result.http_status == PortalDetector::Status::kFailure) |
| retval = kPortalResultHTTPFailure; |
| else if (portal_result.http_status == PortalDetector::Status::kTimeout) |
| retval = kPortalResultHTTPTimeout; |
| else |
| LOG(DFATAL) << __func__ << ": Final result status " |
| << static_cast<int>(portal_result.http_status) |
| << " is not allowed in the HTTP phase"; |
| break; |
| |
| case PortalDetector::Phase::kContent: |
| if (portal_result.http_status == PortalDetector::Status::kSuccess) |
| retval = kPortalResultSuccess; |
| else if (portal_result.http_status == PortalDetector::Status::kFailure) |
| retval = kPortalResultContentFailure; |
| else if (portal_result.http_status == PortalDetector::Status::kRedirect) |
| retval = kPortalResultContentRedirect; |
| else if (portal_result.http_status == PortalDetector::Status::kTimeout) |
| retval = kPortalResultContentTimeout; |
| else |
| LOG(DFATAL) << __func__ << ": Final result status " |
| << static_cast<int>(portal_result.http_status) |
| << " is not allowed in the Content phase"; |
| break; |
| |
| case PortalDetector::Phase::kUnknown: |
| retval = kPortalResultUnknown; |
| break; |
| |
| default: |
| LOG(DFATAL) << __func__ << ": Invalid phase " |
| << static_cast<int>(portal_result.http_phase); |
| break; |
| } |
| |
| return retval; |
| } |
| |
| // static |
| Metrics::NetworkServiceError Metrics::ConnectFailureToServiceErrorEnum( |
| Service::ConnectFailure failure) { |
| // Explicitly map all possible failures. So when new failures are added, |
| // they will need to be mapped as well. Otherwise, the compiler will |
| // complain. |
| switch (failure) { |
| case Service::kFailureNone: |
| return kNetworkServiceErrorNone; |
| case Service::kFailureAAA: |
| return kNetworkServiceErrorAAA; |
| case Service::kFailureActivation: |
| return kNetworkServiceErrorActivation; |
| case Service::kFailureBadPassphrase: |
| return kNetworkServiceErrorBadPassphrase; |
| case Service::kFailureBadWEPKey: |
| return kNetworkServiceErrorBadWEPKey; |
| case Service::kFailureConnect: |
| return kNetworkServiceErrorConnect; |
| case Service::kFailureDHCP: |
| return kNetworkServiceErrorDHCP; |
| case Service::kFailureDNSLookup: |
| return kNetworkServiceErrorDNSLookup; |
| case Service::kFailureEAPAuthentication: |
| return kNetworkServiceErrorEAPAuthentication; |
| case Service::kFailureEAPLocalTLS: |
| return kNetworkServiceErrorEAPLocalTLS; |
| case Service::kFailureEAPRemoteTLS: |
| return kNetworkServiceErrorEAPRemoteTLS; |
| case Service::kFailureHTTPGet: |
| return kNetworkServiceErrorHTTPGet; |
| case Service::kFailureIPsecCertAuth: |
| return kNetworkServiceErrorIPsecCertAuth; |
| case Service::kFailureIPsecPSKAuth: |
| return kNetworkServiceErrorIPsecPSKAuth; |
| case Service::kFailureInternal: |
| return kNetworkServiceErrorInternal; |
| case Service::kFailureNeedEVDO: |
| return kNetworkServiceErrorNeedEVDO; |
| case Service::kFailureNeedHomeNetwork: |
| return kNetworkServiceErrorNeedHomeNetwork; |
| case Service::kFailureNotAssociated: |
| return kNetworkServiceErrorNotAssociated; |
| case Service::kFailureNotAuthenticated: |
| return kNetworkServiceErrorNotAuthenticated; |
| case Service::kFailureOTASP: |
| return kNetworkServiceErrorOTASP; |
| case Service::kFailureOutOfRange: |
| return kNetworkServiceErrorOutOfRange; |
| case Service::kFailurePPPAuth: |
| return kNetworkServiceErrorPPPAuth; |
| case Service::kFailureSimLocked: |
| return kNetworkServiceErrorSimLocked; |
| case Service::kFailureNotRegistered: |
| return kNetworkServiceErrorNotRegistered; |
| case Service::kFailurePinMissing: |
| return kNetworkServiceErrorPinMissing; |
| case Service::kFailureTooManySTAs: |
| return kNetworkServiceErrorTooManySTAs; |
| case Service::kFailureDisconnect: |
| return kNetworkServiceErrorDisconnect; |
| case Service::kFailureUnknown: |
| case Service::kFailureMax: |
| return kNetworkServiceErrorUnknown; |
| } |
| } |
| |
| void Metrics::RegisterService(const Service& service) { |
| SLOG(this, 2) << __func__; |
| LOG_IF(WARNING, base::Contains(services_metrics_, &service)) |
| << "Repeatedly registering " << service.log_name(); |
| services_metrics_[&service] = std::make_unique<ServiceMetrics>(); |
| InitializeCommonServiceMetrics(service); |
| } |
| |
| void Metrics::DeregisterService(const Service& service) { |
| services_metrics_.erase(&service); |
| } |
| |
| void Metrics::AddServiceStateTransitionTimer(const Service& service, |
| const std::string& histogram_name, |
| Service::ConnectState start_state, |
| Service::ConnectState stop_state) { |
| SLOG(this, 2) << __func__ << ": adding " << histogram_name << " for " |
| << Service::ConnectStateToString(start_state) << " -> " |
| << Service::ConnectStateToString(stop_state); |
| ServiceMetricsLookupMap::iterator it = services_metrics_.find(&service); |
| if (it == services_metrics_.end()) { |
| SLOG(this, 1) << "service not found"; |
| DCHECK(false); |
| return; |
| } |
| ServiceMetrics* service_metrics = it->second.get(); |
| CHECK(start_state < stop_state); |
| auto timer = std::make_unique<chromeos_metrics::TimerReporter>( |
| histogram_name, kTimerHistogramMillisecondsMin, |
| kTimerHistogramMillisecondsMax, kTimerHistogramNumBuckets); |
| service_metrics->start_on_state[start_state].push_back(timer.get()); |
| service_metrics->stop_on_state[stop_state].push_back(timer.get()); |
| service_metrics->timers.push_back(std::move(timer)); |
| } |
| |
| void Metrics::OnDefaultLogicalServiceChanged( |
| const ServiceRefPtr& logical_service) { |
| base::TimeDelta elapsed_seconds; |
| Technology technology = logical_service ? logical_service->technology() |
| : Technology(Technology::kUnknown); |
| if (technology != last_default_technology_) { |
| if (last_default_technology_ != Technology::kUnknown) { |
| const auto histogram = GetFullMetricName(kMetricTimeOnlineSecondsSuffix, |
| last_default_technology_); |
| time_online_timer_->GetElapsedTime(&elapsed_seconds); |
| SendToUMA(histogram, elapsed_seconds.InSeconds(), |
| kMetricTimeOnlineSecondsMin, kMetricTimeOnlineSecondsMax, |
| kTimerHistogramNumBuckets); |
| } |
| last_default_technology_ = technology; |
| time_online_timer_->Start(); |
| } |
| |
| // Only consider transitions from online to offline and vice-versa; i.e. |
| // ignore switching between wired and wireless or wireless and cellular. |
| // TimeToDrop measures time online regardless of how we are connected. |
| bool staying_online = ((logical_service != nullptr) && was_last_online_); |
| bool staying_offline = ((logical_service == nullptr) && !was_last_online_); |
| if (staying_online || staying_offline) |
| return; |
| |
| if (logical_service == nullptr) { |
| time_to_drop_timer_->GetElapsedTime(&elapsed_seconds); |
| SendToUMA(kMetricTimeToDropSeconds, elapsed_seconds.InSeconds(), |
| kMetricTimeToDropSecondsMin, kMetricTimeToDropSecondsMax, |
| kTimerHistogramNumBuckets); |
| } else { |
| time_to_drop_timer_->Start(); |
| } |
| |
| was_last_online_ = (logical_service != nullptr); |
| } |
| |
| void Metrics::OnDefaultPhysicalServiceChanged(const ServiceRefPtr&) {} |
| |
| void Metrics::NotifyServiceStateChanged(const Service& service, |
| Service::ConnectState new_state) { |
| ServiceMetricsLookupMap::iterator it = services_metrics_.find(&service); |
| if (it == services_metrics_.end()) { |
| SLOG(this, 1) << "service not found"; |
| DCHECK(false); |
| return; |
| } |
| ServiceMetrics* service_metrics = it->second.get(); |
| UpdateServiceStateTransitionMetrics(service_metrics, new_state); |
| |
| if (new_state == Service::kStateFailure) |
| SendServiceFailure(service); |
| |
| bootstat::BootStat().LogEvent( |
| base::StringPrintf("network-%s-%s", |
| service.technology().GetName().c_str(), |
| service.GetStateString().c_str()) |
| .c_str()); |
| |
| if (new_state != Service::kStateConnected) |
| return; |
| |
| base::TimeDelta time_resume_to_ready; |
| time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready); |
| time_resume_to_ready_timer_->Reset(); |
| service.SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds()); |
| } |
| |
| std::string Metrics::GetFullMetricName(const char* metric_suffix, |
| Technology technology_id) { |
| std::string technology = technology_id.GetName(); |
| technology[0] = base::ToUpperASCII(technology[0]); |
| return base::StringPrintf("%s.%s.%s", kMetricPrefix, technology.c_str(), |
| metric_suffix); |
| } |
| |
| void Metrics::NotifyServiceDisconnect(const Service& service) { |
| Technology technology = service.technology(); |
| const auto histogram = GetFullMetricName(kMetricDisconnectSuffix, technology); |
| SendToUMA(histogram, service.explicitly_disconnected(), kMetricDisconnectMin, |
| kMetricDisconnectMax, kMetricDisconnectNumBuckets); |
| } |
| |
| void Metrics::NotifySignalAtDisconnect(const Service& service, |
| int16_t signal_strength) { |
| // Negate signal_strength (goes from dBm to -dBm) because the metrics don't |
| // seem to handle negative values well. Now everything's positive. |
| Technology technology = service.technology(); |
| const auto histogram = |
| GetFullMetricName(kMetricSignalAtDisconnectSuffix, technology); |
| SendToUMA(histogram, -signal_strength, kMetricSignalAtDisconnectMin, |
| kMetricSignalAtDisconnectMax, kMetricSignalAtDisconnectNumBuckets); |
| } |
| |
| void Metrics::NotifySuspendDone() { |
| time_resume_to_ready_timer_->Start(); |
| } |
| |
| 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; |
| |
| SuspendActionResult result = |
| success ? kSuspendActionResultSuccess : kSuspendActionResultFailure; |
| |
| base::TimeDelta elapsed_time; |
| time_suspend_actions_timer->GetElapsedTime(&elapsed_time); |
| time_suspend_actions_timer->Reset(); |
| std::string time_metric, result_metric; |
| time_metric = kMetricSuspendActionTimeTaken; |
| result_metric = kMetricSuspendActionResult; |
| |
| SendToUMA(time_metric, elapsed_time.InMilliseconds(), |
| kMetricSuspendActionTimeTakenMillisecondsMin, |
| kMetricSuspendActionTimeTakenMillisecondsMax, |
| kTimerHistogramNumBuckets); |
| |
| SendEnumToUMA(result_metric, result, kSuspendActionResultMax); |
| } |
| |
| void Metrics::NotifyNeighborLinkMonitorFailure( |
| Technology technology, |
| IPAddress::Family family, |
| patchpanel::NeighborReachabilityEventSignal::Role role) { |
| const auto histogram = |
| GetFullMetricName(kMetricNeighborLinkMonitorFailureSuffix, technology); |
| NeighborLinkMonitorFailure failure = kNeighborLinkMonitorFailureUnknown; |
| using NeighborSignal = patchpanel::NeighborReachabilityEventSignal; |
| if (family == IPAddress::kFamilyIPv4) { |
| switch (role) { |
| case NeighborSignal::GATEWAY: |
| failure = kNeighborIPv4GatewayFailure; |
| break; |
| case NeighborSignal::DNS_SERVER: |
| failure = kNeighborIPv4DNSServerFailure; |
| break; |
| case NeighborSignal::GATEWAY_AND_DNS_SERVER: |
| failure = kNeighborIPv4GatewayAndDNSServerFailure; |
| break; |
| default: |
| failure = kNeighborLinkMonitorFailureUnknown; |
| } |
| } else if (family == IPAddress::kFamilyIPv6) { |
| switch (role) { |
| case NeighborSignal::GATEWAY: |
| failure = kNeighborIPv6GatewayFailure; |
| break; |
| case NeighborSignal::DNS_SERVER: |
| failure = kNeighborIPv6DNSServerFailure; |
| break; |
| case NeighborSignal::GATEWAY_AND_DNS_SERVER: |
| failure = kNeighborIPv6GatewayAndDNSServerFailure; |
| break; |
| default: |
| failure = kNeighborLinkMonitorFailureUnknown; |
| } |
| } else { |
| LOG(ERROR) << __func__ << " with kFamilyUnknown"; |
| return; |
| } |
| |
| SendEnumToUMA(histogram, failure, kNeighborLinkMonitorFailureMax); |
| } |
| |
| 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, |
| kWiFiApChannelSwitchMax); |
| } |
| |
| 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, kWiFiAp80211rMax); |
| } |
| |
| 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); |
| } |
| |
| #if !defined(DISABLE_WIFI) |
| void Metrics::Notify80211Disconnect(WiFiDisconnectByWhom by_whom, |
| IEEE_80211::WiFiReasonCode reason) { |
| std::string metric_disconnect_reason; |
| std::string 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, IEEE_80211::kReasonCodeMax); |
| SendEnumToUMA(metric_disconnect_type, type, kReasonCodeTypeMax); |
| } |
| #endif // DISABLE_WIFI |
| |
| void Metrics::NotifyWiFiSupplicantAbort() { |
| SendToUMA(kMetricWifiSupplicantAttempts, |
| kMetricWifiSupplicantAttemptsMax, // abort == max |
| kMetricWifiSupplicantAttemptsMin, kMetricWifiSupplicantAttemptsMax, |
| kMetricWifiSupplicantAttemptsNumBuckets); |
| } |
| |
| void Metrics::NotifyWiFiSupplicantSuccess(int attempts) { |
| // Cap "success" at 1 lower than max. Max means we aborted. |
| if (attempts >= kMetricWifiSupplicantAttemptsMax) |
| attempts = kMetricWifiSupplicantAttemptsMax - 1; |
| |
| SendToUMA(kMetricWifiSupplicantAttempts, attempts, |
| kMetricWifiSupplicantAttemptsMin, kMetricWifiSupplicantAttemptsMax, |
| kMetricWifiSupplicantAttemptsNumBuckets); |
| } |
| |
| void Metrics::RegisterDevice(int interface_index, Technology technology) { |
| SLOG(this, 2) << __func__ << ": " << interface_index; |
| |
| if (technology.IsPrimaryConnectivityTechnology()) { |
| bootstat::BootStat().LogEvent( |
| base::StringPrintf("network-%s-registered", |
| technology.GetName().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(this, 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(this, 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; |
| device_metrics->initialization_timer->ReportMilliseconds(); |
| } |
| |
| 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; |
| device_metrics->enable_timer->ReportMilliseconds(); |
| } |
| |
| 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; |
| device_metrics->disable_timer->ReportMilliseconds(); |
| } |
| |
| 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) |
| device_metrics->scan_timer->ReportMilliseconds(); |
| } |
| |
| 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; |
| device_metrics->connect_timer->ReportMilliseconds(); |
| |
| if (!device_metrics->scan_connect_timer->Stop()) |
| return; |
| device_metrics->scan_connect_timer->ReportMilliseconds(); |
| } |
| |
| 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::Notify3GPPRegistrationDelayedDropPosted() { |
| SendEnumToUMA(kMetricCellular3GPPRegistrationDelayedDrop, |
| kCellular3GPPRegistrationDelayedDropPosted, |
| kCellular3GPPRegistrationDelayedDropMax); |
| } |
| |
| void Metrics::Notify3GPPRegistrationDelayedDropCanceled() { |
| SendEnumToUMA(kMetricCellular3GPPRegistrationDelayedDrop, |
| kCellular3GPPRegistrationDelayedDropCanceled, |
| kCellular3GPPRegistrationDelayedDropMax); |
| } |
| |
| void Metrics::NotifyCellularDeviceDrop(const std::string& network_technology, |
| uint16_t signal_strength) { |
| SLOG(this, 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, |
| kCellularDropTechnologyMax); |
| SendToUMA(kMetricCellularSignalStrengthBeforeDrop, signal_strength, |
| kMetricCellularSignalStrengthBeforeDropMin, |
| kMetricCellularSignalStrengthBeforeDropMax, |
| kMetricCellularSignalStrengthBeforeDropNumBuckets); |
| } |
| |
| void Metrics::NotifyCellularConnectionResult(Error::Type error) { |
| SLOG(this, 2) << __func__ << ": " << error; |
| |
| CellularConnectResult connect_result = |
| ConvertErrorToCellularConnectResult(error); |
| |
| SendEnumToUMA( |
| kMetricCellularConnectResult, static_cast<int>(connect_result), |
| static_cast<int>(CellularConnectResult::kCellularConnectResultMax)); |
| } |
| |
| 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; |
| } |
| |
| void Metrics::NotifyDetailedCellularConnectionResult( |
| Error::Type error, |
| const std::string& detailed_error, |
| const std::string& uuid, |
| const shill::Stringmap& apn_info, |
| IPConfig::Method ipv4_config_method, |
| IPConfig::Method ipv6_config_method, |
| const std::string& home_mccmnc, |
| const std::string& serving_mccmnc, |
| const std::string& roaming_state, |
| bool use_attach_apn, |
| uint32_t tech_used, |
| uint32_t iccid_length, |
| uint32_t sim_type, |
| uint32_t modem_state, |
| int interface_index) { |
| 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(error); |
| uint32_t connect_time = 0; |
| uint32_t scan_connect_time = 0; |
| DeviceMetrics* device_metrics = GetDeviceMetrics(interface_index); |
| |
| base::StringToInt64(home_mccmnc, &home); |
| base::StringToInt64(serving_mccmnc, &serving); |
| crypto::SHA256HashString(detailed_error, &detailed_error_hash, 8); |
| |
| if (roaming_state == kRoamingStateHome) |
| roaming = kCellularRoamingStateHome; |
| else if (roaming_state == kRoamingStateRoaming) |
| roaming = kCellularRoamingStateRoaming; |
| |
| DCHECK(base::Contains(apn_info, cellular::kApnSource)); |
| if (base::Contains(apn_info, cellular::kApnSource)) { |
| if (apn_info.at(cellular::kApnSource) == cellular::kApnSourceMoDb) |
| apn_source = kCellularApnSourceMoDb; |
| else if (apn_info.at(cellular::kApnSource) == cellular::kApnSourceUi) |
| apn_source = kCellularApnSourceUi; |
| else if (apn_info.at(cellular::kApnSource) == cellular::kApnSourceModem) |
| apn_source = kCellularApnSourceModem; |
| |
| if (apn_info.at(cellular::kApnSource) == cellular::kApnSourceMoDb || |
| apn_info.at(cellular::kApnSource) == cellular::kApnSourceModem) { |
| if (base::Contains(apn_info, kApnProperty)) |
| apn_name = apn_info.at(kApnProperty); |
| if (base::Contains(apn_info, kApnUsernameProperty)) |
| username = apn_info.at(kApnUsernameProperty); |
| if (base::Contains(apn_info, kApnPasswordProperty)) |
| password = apn_info.at(kApnPasswordProperty); |
| } |
| } |
| |
| 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(this, 3) << __func__ << ": error:" << error << " uuid:" << uuid |
| << " apn:" << apn_name << " apn_source:" << apn_source |
| << " ipv4:" << ipv4_config_method |
| << " ipv6:" << ipv6_config_method |
| << " home_mccmnc:" << home_mccmnc |
| << " serving_mccmnc:" << serving_mccmnc |
| << " roaming_state:" << roaming_state |
| << " tech_used:" << tech_used |
| << " iccid_length:" << iccid_length << " sim_type:" << sim_type |
| << " modem_state:" << modem_state |
| << " connect_time:" << connect_time |
| << " scan_connect_time:" << scan_connect_time |
| << " detailed_error:" << detailed_error; |
| |
| metrics::structured::events::cellular::CellularConnectionAttempt() |
| .Setconnect_result(static_cast<int64_t>(connect_result)) |
| .Setapn_id(HashApn(uuid, apn_name, username, password)) |
| .Setipv4_config_method(ipv4_config_method) |
| .Setipv6_config_method(ipv6_config_method) |
| .Sethome_mccmnc(home) |
| .Setserving_mccmnc(serving) |
| .Setroaming_state(roaming) |
| .Setuse_attach_apn(use_attach_apn) |
| .Setapn_source(static_cast<int64_t>(apn_source)) |
| .Settech_used(tech_used) |
| .Seticcid_length(iccid_length) |
| .Setsim_type(sim_type) |
| .Setmodem_state(modem_state) |
| .Setconnect_time(connect_time) |
| .Setscan_connect_time(scan_connect_time) |
| .Setdetailed_error(detailed_error_hash) |
| .Record(); |
| } |
| |
| void Metrics::NotifyCellularOutOfCredits( |
| Metrics::CellularOutOfCreditsReason reason) { |
| SendEnumToUMA(kMetricCellularOutOfCreditsReason, reason, |
| kCellularOutOfCreditsReasonMax); |
| } |
| |
| void Metrics::NotifyCorruptedProfile() { |
| SendEnumToUMA(kMetricCorruptedProfile, kCorruptedProfile, |
| kCorruptedProfileMax); |
| } |
| |
| void Metrics::NotifyWifiAutoConnectableServices(int num_services) { |
| SendToUMA(kMetricWifiAutoConnectableServices, num_services, |
| kMetricWifiAutoConnectableServicesMin, |
| kMetricWifiAutoConnectableServicesMax, |
| kMetricWifiAutoConnectableServicesNumBuckets); |
| } |
| |
| void Metrics::NotifyWifiAvailableBSSes(int num_bss) { |
| SendToUMA(kMetricWifiAvailableBSSes, num_bss, kMetricWifiAvailableBSSesMin, |
| kMetricWifiAvailableBSSesMax, kMetricWifiAvailableBSSesNumBuckets); |
| } |
| |
| void Metrics::NotifyWifiTxBitrate(int bitrate) { |
| SendToUMA(kMetricWifiTxBitrate, bitrate, kMetricWifiTxBitrateMin, |
| kMetricWifiTxBitrateMax, kMetricWifiTxBitrateNumBuckets); |
| } |
| |
| void Metrics::NotifyUserInitiatedConnectionResult(const std::string& name, |
| int result) { |
| SendEnumToUMA(name, result, kUserInitiatedConnectionResultMax); |
| } |
| |
| void Metrics::NotifyUserInitiatedConnectionFailureReason( |
| const std::string& name, const Service::ConnectFailure failure) { |
| UserInitiatedConnectionFailureReason reason; |
| switch (failure) { |
| case Service::kFailureNone: |
| reason = kUserInitiatedConnectionFailureReasonNone; |
| break; |
| case Service::kFailureBadPassphrase: |
| reason = kUserInitiatedConnectionFailureReasonBadPassphrase; |
| break; |
| case Service::kFailureBadWEPKey: |
| reason = kUserInitiatedConnectionFailureReasonBadWEPKey; |
| break; |
| case Service::kFailureConnect: |
| reason = kUserInitiatedConnectionFailureReasonConnect; |
| break; |
| case Service::kFailureDHCP: |
| reason = kUserInitiatedConnectionFailureReasonDHCP; |
| break; |
| case Service::kFailureDNSLookup: |
| reason = kUserInitiatedConnectionFailureReasonDNSLookup; |
| break; |
| case Service::kFailureEAPAuthentication: |
| reason = kUserInitiatedConnectionFailureReasonEAPAuthentication; |
| break; |
| case Service::kFailureEAPLocalTLS: |
| reason = kUserInitiatedConnectionFailureReasonEAPLocalTLS; |
| break; |
| case Service::kFailureEAPRemoteTLS: |
| reason = kUserInitiatedConnectionFailureReasonEAPRemoteTLS; |
| break; |
| case Service::kFailureNotAssociated: |
| reason = kUserInitiatedConnectionFailureReasonNotAssociated; |
| break; |
| case Service::kFailureNotAuthenticated: |
| reason = kUserInitiatedConnectionFailureReasonNotAuthenticated; |
| break; |
| case Service::kFailureOutOfRange: |
| reason = kUserInitiatedConnectionFailureReasonOutOfRange; |
| break; |
| case Service::kFailurePinMissing: |
| reason = kUserInitiatedConnectionFailureReasonPinMissing; |
| break; |
| case Service::kFailureTooManySTAs: |
| reason = kUserInitiatedConnectionFailureReasonTooManySTAs; |
| break; |
| default: |
| reason = kUserInitiatedConnectionFailureReasonUnknown; |
| break; |
| } |
| SendEnumToUMA(name, reason, kUserInitiatedConnectionFailureReasonMax); |
| } |
| |
| void Metrics::NotifyDeviceConnectionStatus(ConnectionStatus status) { |
| SendEnumToUMA(kMetricDeviceConnectionStatus, status, kConnectionStatusMax); |
| } |
| |
| void Metrics::NotifyNetworkConnectionIPType(Technology technology_id, |
| NetworkConnectionIPType type) { |
| const auto histogram = |
| GetFullMetricName(kMetricNetworkConnectionIPTypeSuffix, technology_id); |
| SendEnumToUMA(histogram, type, kNetworkConnectionIPTypeMax); |
| } |
| |
| void Metrics::NotifyIPv6ConnectivityStatus(Technology technology_id, |
| bool status) { |
| const auto histogram = |
| GetFullMetricName(kMetricIPv6ConnectivityStatusSuffix, technology_id); |
| IPv6ConnectivityStatus ipv6_status = |
| status ? kIPv6ConnectivityStatusYes : kIPv6ConnectivityStatusNo; |
| SendEnumToUMA(histogram, ipv6_status, kIPv6ConnectivityStatusMax); |
| } |
| |
| void Metrics::NotifyDevicePresenceStatus(Technology technology_id, |
| bool status) { |
| const auto histogram = |
| GetFullMetricName(kMetricDevicePresenceStatusSuffix, technology_id); |
| DevicePresenceStatus presence = |
| status ? kDevicePresenceStatusYes : kDevicePresenceStatusNo; |
| SendEnumToUMA(histogram, presence, kDevicePresenceStatusMax); |
| } |
| |
| void Metrics::NotifyUnreliableLinkSignalStrength(Technology technology_id, |
| int signal_strength) { |
| const auto histogram = GetFullMetricName( |
| kMetricUnreliableLinkSignalStrengthSuffix, technology_id); |
| SendToUMA(histogram, signal_strength, kMetricServiceSignalStrengthMin, |
| kMetricServiceSignalStrengthMax, |
| kMetricServiceSignalStrengthNumBuckets); |
| } |
| |
| bool Metrics::SendEnumToUMA(const std::string& name, int sample, int max) { |
| SLOG(this, 5) << "Sending enum " << name << " with value " << sample << "."; |
| return library_->SendEnumToUMA(name, sample, max); |
| } |
| |
| bool Metrics::SendBoolToUMA(const std::string& name, bool b) { |
| SLOG(this, 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(this, 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(this, 5) << "Sending sparse metric " << name << " with value " << sample |
| << "."; |
| return library_->SendSparseToUMA(name, sample); |
| } |
| |
| 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::kIssueHTTPBrokenPortal) { |
| issue_enum = kConnectionDiagnosticsIssueHTTPBrokenPortal; |
| } 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::kIssueCaptivePortal) { |
| issue_enum = kConnectionDiagnosticsIssueCaptivePortal; |
| } 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, |
| kConnectionDiagnosticsIssueMax); |
| } |
| |
| void Metrics::NotifyPortalDetectionMultiProbeResult( |
| const PortalDetector::Result& result) { |
| // kTimeout is implicitly treated as a failure |
| // kRedirect on HTTPS is unexpected and ignored |
| PortalDetectionMultiProbeResult result_enum; |
| if (result.https_status == PortalDetector::Status::kRedirect) { |
| result_enum = kPortalDetectionMultiProbeResultUndefined; |
| } else if (result.https_status != PortalDetector::Status::kSuccess && |
| result.http_status == PortalDetector::Status::kSuccess) { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSBlockedHTTPUnblocked; |
| } else if (result.https_status != PortalDetector::Status::kSuccess && |
| result.http_status == PortalDetector::Status::kRedirect) { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSBlockedHTTPRedirected; |
| } else if (result.https_status != PortalDetector::Status::kSuccess) { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSBlockedHTTPBlocked; |
| } else if (result.https_status == PortalDetector::Status::kSuccess && |
| result.http_status == PortalDetector::Status::kSuccess) { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSUnblockedHTTPUnblocked; |
| } else if (result.https_status == PortalDetector::Status::kSuccess && |
| result.http_status == PortalDetector::Status::kRedirect) { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSUnblockedHTTPRedirected; |
| } else { |
| result_enum = kPortalDetectionMultiProbeResultHTTPSUnblockedHTTPBlocked; |
| } |
| |
| SendEnumToUMA(kMetricPortalDetectionMultiProbeResult, result_enum, |
| kPortalDetectionMultiProbeResultMax); |
| } |
| |
| void Metrics::NotifyHS20Support(bool hs20_supported, int hs20_version_number) { |
| if (!hs20_supported) { |
| SendEnumToUMA(kMetricHS20Support, kHS20Unsupported, kHS20SupportMax); |
| 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, kHS20SupportMax); |
| } |
| |
| void Metrics::NotifyMBOSupport(bool mbo_support) { |
| SendBoolToUMA(kMetricMBOSupport, mbo_support); |
| } |
| |
| void Metrics::NotifyWiFiServiceFailureAfterRekey(int seconds) { |
| SendToUMA(kMetricTimeFromRekeyToFailureSeconds, seconds, |
| kMetricTimeFromRekeyToFailureSecondsMin, |
| kMetricTimeFromRekeyToFailureSecondsMax, |
| kMetricTimeFromRekeyToFailureSecondsNumBuckets); |
| } |
| |
| void Metrics::NotifyWiFiAdapterStateChanged(bool enabled, |
| int vendor_id, |
| int product_id, |
| int subsystem_id) { |
| int64_t usecs; |
| if (!time_ || !time_->GetMicroSecondsMonotonic(&usecs)) { |
| LOG(ERROR) << "Failed to read timestamp"; |
| usecs = kWiFiStructuredMetricsErrorValue; |
| } |
| metrics::structured::events::wi_fi_chipset::WiFiChipsetInfo() |
| .SetEventVersion(kWiFiStructuredMetricsVersion) |
| .SetVendorId(vendor_id) |
| .SetProductId(product_id) |
| .SetSubsystemId(subsystem_id) |
| .Record(); |
| metrics::structured::events::wi_fi::WiFiAdapterStateChanged() |
| .SetBootId(GetBootId()) |
| .SetSystemTime(usecs) |
| .SetEventVersion(kWiFiStructuredMetricsVersion) |
| .SetAdapterState(enabled) |
| .SetVendorId(Metrics::kWiFiStructuredMetricsErrorValue) |
| .SetProductId(Metrics::kWiFiStructuredMetricsErrorValue) |
| .SetSubsystemId(Metrics::kWiFiStructuredMetricsErrorValue) |
| .Record(); |
| } |
| |
| // static |
| Metrics::WiFiConnectionAttemptInfo::ApSupportedFeatures |
| Metrics::ConvertEndPointFeatures(const WiFiEndpoint* ep) { |
| Metrics::WiFiConnectionAttemptInfo::ApSupportedFeatures ap_features; |
| if (ep) { |
| ap_features.krv_info.neighbor_list_supported = |
| ep->krv_support().neighbor_list_supported; |
| ap_features.krv_info.ota_ft_supported = ep->krv_support().ota_ft_supported; |
| ap_features.krv_info.otds_ft_supported = |
| ep->krv_support().otds_ft_supported; |
| ap_features.krv_info.dms_supported = ep->krv_support().dms_supported; |
| ap_features.krv_info.bss_max_idle_period_supported = |
| ep->krv_support().bss_max_idle_period_supported; |
| ap_features.krv_info.bss_transition_supported = |
| ep->krv_support().bss_transition_supported; |
| |
| ap_features.hs20_info.supported = ep->hs20_information().supported; |
| ap_features.hs20_info.version = ep->hs20_information().version; |
| |
| ap_features.mbo_supported = ep->mbo_support(); |
| } |
| return ap_features; |
| } |
| |
| void Metrics::NotifyWiFiConnectionAttempt( |
| const WiFiConnectionAttemptInfo& info) { |
| int64_t usecs; |
| if (!time_ || !time_->GetMicroSecondsMonotonic(&usecs)) { |
| LOG(ERROR) << "Failed to read timestamp"; |
| usecs = kWiFiStructuredMetricsErrorValue; |
| } |
| metrics::structured::events::wi_fi::WiFiConnectionAttempt() |
| .SetBootId(GetBootId()) |
| .SetSystemTime(usecs) |
| .SetEventVersion(kWiFiStructuredMetricsVersion) |
| .SetAttemptType(info.type) |
| .SetAPPhyMode(info.mode) |
| .SetAPSecurityMode(info.security) |
| .SetAPSecurityEAPInnerProtocol(info.eap_inner) |
| .SetAPSecurityEAPOuterProtocol(info.eap_outer) |
| .SetAPChannel(info.channel) |
| .SetRSSI(info.rssi) |
| .SetSSID(info.ssid) |
| .SetSSIDProvisioningMode(info.provisioning_mode) |
| .SetSSIDHidden(info.ssid_hidden) |
| .SetBSSID(info.bssid) |
| .SetAPOUI(info.ap_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( |
| NetworkServiceError result_code) { |
| int64_t usecs; |
| if (!time_ || !time_->GetMicroSecondsMonotonic(&usecs)) { |
| LOG(ERROR) << "Failed to read timestamp"; |
| usecs = kWiFiStructuredMetricsErrorValue; |
| } |
| metrics::structured::events::wi_fi::WiFiConnectionAttemptResult() |
| .SetBootId(GetBootId()) |
| .SetSystemTime(usecs) |
| .SetEventVersion(kWiFiStructuredMetricsVersion) |
| .SetResultCode(result_code) |
| .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); |
| } |
| } |
| |
| void Metrics::InitializeCommonServiceMetrics(const Service& service) { |
| Technology technology = service.technology(); |
| auto histogram = |
| GetFullMetricName(kMetricTimeToConfigMillisecondsSuffix, technology); |
| AddServiceStateTransitionTimer(service, histogram, Service::kStateConfiguring, |
| Service::kStateConnected); |
| histogram = |
| GetFullMetricName(kMetricTimeToPortalMillisecondsSuffix, technology); |
| AddServiceStateTransitionTimer(service, histogram, Service::kStateConnected, |
| Service::kStateNoConnectivity); |
| histogram = GetFullMetricName(kMetricTimeToRedirectFoundMillisecondsSuffix, |
| technology); |
| AddServiceStateTransitionTimer(service, histogram, Service::kStateConnected, |
| Service::kStateRedirectFound); |
| histogram = |
| GetFullMetricName(kMetricTimeToOnlineMillisecondsSuffix, technology); |
| AddServiceStateTransitionTimer(service, histogram, Service::kStateConnected, |
| Service::kStateOnline); |
| } |
| |
| void Metrics::UpdateServiceStateTransitionMetrics( |
| ServiceMetrics* service_metrics, Service::ConnectState new_state) { |
| const char* state_string = Service::ConnectStateToString(new_state); |
| SLOG(this, 5) << __func__ << ": new_state=" << state_string; |
| TimerReportersList& start_timers = service_metrics->start_on_state[new_state]; |
| for (auto& start_timer : start_timers) { |
| SLOG(this, 5) << "Starting timer for " << start_timer->histogram_name() |
| << " due to new state " << state_string << "."; |
| start_timer->Start(); |
| } |
| |
| TimerReportersList& stop_timers = service_metrics->stop_on_state[new_state]; |
| for (auto& stop_timer : stop_timers) { |
| SLOG(this, 5) << "Stopping timer for " << stop_timer->histogram_name() |
| << " due to new state " << state_string << "."; |
| if (stop_timer->Stop()) |
| stop_timer->ReportMilliseconds(); |
| } |
| } |
| |
| void Metrics::SendServiceFailure(const Service& service) { |
| NetworkServiceError error = |
| ConnectFailureToServiceErrorEnum(service.failure()); |
| |
| const auto histogram = |
| GetFullMetricName(kMetricNetworkServiceErrorSuffix, service.technology()); |
| |
| // Publish technology specific connection failure metrics. This will |
| // account for all the connection failures happening while connected to |
| // a particular interface e.g. wifi, cellular etc. |
| library_->SendEnumToUMA(histogram, error, kNetworkServiceErrorMax); |
| } |
| |
| Metrics::DeviceMetrics* Metrics::GetDeviceMetrics(int interface_index) const { |
| DeviceMetricsLookupMap::const_iterator it = |
| devices_metrics_.find(interface_index); |
| if (it == devices_metrics_.end()) { |
| SLOG(this, 2) << __func__ << ": device " << interface_index << " not found"; |
| return nullptr; |
| } |
| return it->second.get(); |
| } |
| |
| // static |
| std::string Metrics::GetBootId() { |
| std::string boot_id; |
| if (!base::ReadFileToString(base::FilePath(Metrics::kBootIdProcPath), |
| &boot_id)) { |
| LOG(ERROR) << "Failed to read boot_id"; |
| return std::string(); |
| } |
| base::RemoveChars(boot_id, "-\r\n", &boot_id); |
| return boot_id; |
| } |
| |
| void Metrics::set_library(MetricsLibraryInterface* library) { |
| chromeos_metrics::TimerReporter::set_metrics_lib(library); |
| library_ = library; |
| } |
| |
| } // namespace shill |