blob: 4162f64693d13b32fdcec108c988551c2c38c1ad [file] [log] [blame]
Thieu Lead1ec2c2012-01-05 23:39:48 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Thieu Le48e6d6d2011-12-06 00:40:27 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/metrics.h"
6
Thieu Le48e6d6d2011-12-06 00:40:27 +00007#include <base/logging.h>
8#include <base/string_util.h>
9#include <base/stringprintf.h>
Thieu Lead1ec2c2012-01-05 23:39:48 +000010#include <chromeos/dbus/service_constants.h>
Darin Petkov58f0b6d2012-06-12 12:52:30 +020011#include <metrics/bootstat.h>
Thieu Le48e6d6d2011-12-06 00:40:27 +000012
Paul Stewartff845fc2012-08-07 07:28:44 -070013#include "shill/link_monitor.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070014#include "shill/scope_logger.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000015#include "shill/wifi_service.h"
16
17using std::string;
18using std::tr1::shared_ptr;
19
20namespace shill {
21
Thieu Le48e6d6d2011-12-06 00:40:27 +000022// static
Thieu Lec31e6f92012-08-03 13:08:58 -070023// Our disconnect enumeration values are 0 (System Disconnect) and
24// 1 (User Disconnect), see histograms.xml, but Chrome needs a minimum
25// enum value of 1 and the minimum number of buckets needs to be 3 (see
26// histogram.h). Instead of remapping System Disconnect to 1 and
27// User Disconnect to 2, we can just leave the enumerated values as-is
28// because Chrome implicitly creates a [0-1) bucket for us. Using Min=1,
29// Max=2 and NumBuckets=3 gives us the following three buckets:
30// [0-1), [1-2), [2-INT_MAX). We end up with an extra bucket [2-INT_MAX)
31// that we can safely ignore.
Thieu Le67370f62012-02-14 23:01:42 +000032const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
Thieu Lec31e6f92012-08-03 13:08:58 -070033const int Metrics::kMetricDisconnectMax = 2;
34const int Metrics::kMetricDisconnectMin = 1;
35const int Metrics::kMetricDisconnectNumBuckets = 3;
Thieu Le67370f62012-02-14 23:01:42 +000036
Thieu Le48e6d6d2011-12-06 00:40:27 +000037const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
38const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000039const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
40const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
41const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
42const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000043const char Metrics::kMetricNetworkServiceErrors[] =
44 "Network.Shill.ServiceErrors";
45const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000046
47const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
48const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
49const int Metrics::kMetricTimeOnlineSecondsMin = 1;
50
51const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
52const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
53const int Metrics::kMetricTimeToDropSecondsMin = 1;
54
Thieu Leb84ba342012-03-02 15:15:19 -080055const char Metrics::kMetricTimeResumeToReadyMilliseconds[] =
56 "Network.Shill.%s.TimeResumeToReady";
Thieu Le48e6d6d2011-12-06 00:40:27 +000057const char Metrics::kMetricTimeToConfigMilliseconds[] =
58 "Network.Shill.%s.TimeToConfig";
59const char Metrics::kMetricTimeToJoinMilliseconds[] =
60 "Network.Shill.%s.TimeToJoin";
61const char Metrics::kMetricTimeToOnlineMilliseconds[] =
62 "Network.Shill.%s.TimeToOnline";
63const char Metrics::kMetricTimeToPortalMilliseconds[] =
64 "Network.Shill.%s.TimeToPortal";
Thieu Lea20cbc22012-01-09 22:01:43 +000065const int Metrics::kTimerHistogramMillisecondsMax = 45 * 1000;
66const int Metrics::kTimerHistogramMillisecondsMin = 1;
Thieu Le48e6d6d2011-12-06 00:40:27 +000067const int Metrics::kTimerHistogramNumBuckets = 50;
68
Thieu Le85e050b2012-03-13 15:04:38 -070069const char Metrics::kMetricPortalAttempts[] =
70 "Network.Shill.%s.PortalAttempts";
71const int Metrics::kMetricPortalAttemptsMax =
72 PortalDetector::kMaxRequestAttempts;
73const int Metrics::kMetricPortalAttemptsMin = 1;
74const int Metrics::kMetricPortalAttemptsNumBuckets =
75 Metrics::kMetricPortalAttemptsMax;
76
77const char Metrics::kMetricPortalAttemptsToOnline[] =
78 "Network.Shill.%s.PortalAttemptsToOnline";
79const int Metrics::kMetricPortalAttemptsToOnlineMax = 100;
80const int Metrics::kMetricPortalAttemptsToOnlineMin = 1;
81const int Metrics::kMetricPortalAttemptsToOnlineNumBuckets = 10;
82
83const char Metrics::kMetricPortalResult[] = "Network.Shill.%s.PortalResult";
84
Thieu Le48e6d6d2011-12-06 00:40:27 +000085// static
86const uint16 Metrics::kWiFiBandwidth5MHz = 5;
87const uint16 Metrics::kWiFiBandwidth20MHz = 20;
88const uint16 Metrics::kWiFiFrequency2412 = 2412;
89const uint16 Metrics::kWiFiFrequency2472 = 2472;
90const uint16 Metrics::kWiFiFrequency2484 = 2484;
91const uint16 Metrics::kWiFiFrequency5170 = 5170;
92const uint16 Metrics::kWiFiFrequency5180 = 5180;
93const uint16 Metrics::kWiFiFrequency5230 = 5230;
94const uint16 Metrics::kWiFiFrequency5240 = 5240;
95const uint16 Metrics::kWiFiFrequency5320 = 5320;
96const uint16 Metrics::kWiFiFrequency5500 = 5500;
97const uint16 Metrics::kWiFiFrequency5700 = 5700;
98const uint16 Metrics::kWiFiFrequency5745 = 5745;
99const uint16 Metrics::kWiFiFrequency5825 = 5825;
100
Thieu Leb84ba342012-03-02 15:15:19 -0800101// static
102const char Metrics::kMetricPowerManagerKey[] = "metrics";
103
Paul Stewartff845fc2012-08-07 07:28:44 -0700104// static
105const char Metrics::kMetricLinkMonitorFailure[] =
106 "Network.Shill.%s.LinkMonitorFailure";
107const char Metrics::kMetricLinkMonitorResponseTimeSample[] =
108 "Network.Shill.%s.LinkMonitorResponseTimeSample";
109const int Metrics::kMetricLinkMonitorResponseTimeSampleMin = 0;
110const int Metrics::kMetricLinkMonitorResponseTimeSampleMax =
111 LinkMonitor::kTestPeriodMilliseconds;
112const int Metrics::kMetricLinkMonitorResponseTimeSampleNumBuckets = 50;
113
114
Thieu Lea20cbc22012-01-09 22:01:43 +0000115Metrics::Metrics()
116 : library_(&metrics_library_),
117 last_default_technology_(Technology::kUnknown),
118 was_online_(false),
119 time_online_timer_(new chromeos_metrics::Timer),
Thieu Leb84ba342012-03-02 15:15:19 -0800120 time_to_drop_timer_(new chromeos_metrics::Timer),
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200121 time_resume_to_ready_timer_(new chromeos_metrics::Timer),
122 collect_bootstats_(true) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000123 metrics_library_.Init();
124 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
125}
126
127Metrics::~Metrics() {}
128
129// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000130Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
131 WiFiChannel channel = kWiFiChannelUndef;
132 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
133 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
134 channel = static_cast<WiFiChannel>(
135 kWiFiChannel2412 +
136 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
137 } else if (frequency == kWiFiFrequency2484) {
138 channel = kWiFiChannel2484;
139 } else if (kWiFiFrequency5170 <= frequency &&
140 frequency <= kWiFiFrequency5230) {
141 if ((frequency % kWiFiBandwidth20MHz) == 0)
142 channel = static_cast<WiFiChannel>(
143 kWiFiChannel5180 +
144 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
145 if ((frequency % kWiFiBandwidth20MHz) == 10)
146 channel = static_cast<WiFiChannel>(
147 kWiFiChannel5170 +
148 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
149 } else if (kWiFiFrequency5240 <= frequency &&
150 frequency <= kWiFiFrequency5320) {
151 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
152 channel = static_cast<WiFiChannel>(
153 kWiFiChannel5180 +
154 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
155 } else if (kWiFiFrequency5500 <= frequency &&
156 frequency <= kWiFiFrequency5700) {
157 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
158 channel = static_cast<WiFiChannel>(
159 kWiFiChannel5500 +
160 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
161 } else if (kWiFiFrequency5745 <= frequency &&
162 frequency <= kWiFiFrequency5825) {
163 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
164 channel = static_cast<WiFiChannel>(
165 kWiFiChannel5745 +
166 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
167 }
168 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
169
170 if (channel == kWiFiChannelUndef)
171 LOG(WARNING) << "no mapping for frequency " << frequency;
172 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700173 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000174
175 return channel;
176}
177
Thieu Lead1ec2c2012-01-05 23:39:48 +0000178// static
179Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
180 const std::string &security) {
181 if (security == flimflam::kSecurityNone) {
182 return kWiFiSecurityNone;
183 } else if (security == flimflam::kSecurityWep) {
184 return kWiFiSecurityWep;
185 } else if (security == flimflam::kSecurityWpa) {
186 return kWiFiSecurityWpa;
187 } else if (security == flimflam::kSecurityRsn) {
188 return kWiFiSecurityRsn;
189 } else if (security == flimflam::kSecurity8021x) {
190 return kWiFiSecurity8021x;
191 } else if (security == flimflam::kSecurityPsk) {
192 return kWiFiSecurityPsk;
193 } else {
194 return kWiFiSecurityUnknown;
195 }
196}
197
Thieu Le85e050b2012-03-13 15:04:38 -0700198// static
199Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
200 const PortalDetector::Result &result) {
201 DCHECK(result.final);
202 PortalResult retval = kPortalResultUnknown;
203 // The only time we should end a successful portal detection is when we're
204 // in the Content phase. If we end with kStatusSuccess in any other phase,
205 // then this indicates that something bad has happened.
206 switch (result.phase) {
207 case PortalDetector::kPhaseDNS:
208 if (result.status == PortalDetector::kStatusFailure)
209 retval = kPortalResultDNSFailure;
210 else if (result.status == PortalDetector::kStatusTimeout)
211 retval = kPortalResultDNSTimeout;
212 else
213 LOG(DFATAL) << __func__ << ": Final result status " << result.status
214 << " is not allowed in the DNS phase";
215 break;
216
217 case PortalDetector::kPhaseConnection:
218 if (result.status == PortalDetector::kStatusFailure)
219 retval = kPortalResultConnectionFailure;
220 else if (result.status == PortalDetector::kStatusTimeout)
221 retval = kPortalResultConnectionTimeout;
222 else
223 LOG(DFATAL) << __func__ << ": Final result status " << result.status
224 << " is not allowed in the Connection phase";
225 break;
226
227 case PortalDetector::kPhaseHTTP:
228 if (result.status == PortalDetector::kStatusFailure)
229 retval = kPortalResultHTTPFailure;
230 else if (result.status == PortalDetector::kStatusTimeout)
231 retval = kPortalResultHTTPTimeout;
232 else
233 LOG(DFATAL) << __func__ << ": Final result status " << result.status
234 << " is not allowed in the HTTP phase";
235 break;
236
237 case PortalDetector::kPhaseContent:
238 if (result.status == PortalDetector::kStatusSuccess)
239 retval = kPortalResultSuccess;
240 else if (result.status == PortalDetector::kStatusFailure)
241 retval = kPortalResultContentFailure;
242 else if (result.status == PortalDetector::kStatusTimeout)
243 retval = kPortalResultContentTimeout;
244 else
245 LOG(DFATAL) << __func__ << ": Final result status " << result.status
246 << " is not allowed in the Content phase";
247 break;
248
249 case PortalDetector::kPhaseUnknown:
250 retval = kPortalResultUnknown;
251 break;
252
253 default:
254 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
255 break;
256 }
257
258 return retval;
259}
260
Thieu Le48e6d6d2011-12-06 00:40:27 +0000261void Metrics::RegisterService(const Service *service) {
262 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
263 services_metrics_[service] = service_metrics;
264 service_metrics->service = service;
265 InitializeCommonServiceMetrics(service);
266 service->InitializeCustomMetrics();
267}
268
269void Metrics::DeregisterService(const Service *service) {
270 services_metrics_.erase(service);
271}
272
273void Metrics::AddServiceStateTransitionTimer(
274 const Service *service,
275 const string &histogram_name,
276 Service::ConnectState start_state,
277 Service::ConnectState stop_state) {
278 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
279 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700280 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000281 DCHECK(false);
282 return;
283 }
284 ServiceMetrics *service_metrics = it->second.get();
285 CHECK(start_state < stop_state);
286 chromeos_metrics::TimerReporter *timer =
287 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000288 kTimerHistogramMillisecondsMin,
289 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000290 kTimerHistogramNumBuckets);
291 service_metrics->timers.push_back(timer); // passes ownership.
292 service_metrics->start_on_state[start_state].push_back(timer);
293 service_metrics->stop_on_state[stop_state].push_back(timer);
294}
295
Thieu Lea20cbc22012-01-09 22:01:43 +0000296void Metrics::NotifyDefaultServiceChanged(const Service *service) {
297 base::TimeDelta elapsed_seconds;
298
299 Technology::Identifier technology = (service) ? service->technology() :
300 Technology::kUnknown;
301 if (technology != last_default_technology_) {
302 if (last_default_technology_ != Technology::kUnknown) {
303 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
304 last_default_technology_);
305 time_online_timer_->GetElapsedTime(&elapsed_seconds);
306 SendToUMA(histogram,
307 elapsed_seconds.InSeconds(),
308 kMetricTimeOnlineSecondsMin,
309 kMetricTimeOnlineSecondsMax,
310 kTimerHistogramNumBuckets);
311 }
312 last_default_technology_ = technology;
313 time_online_timer_->Start();
314 }
315
Thieu Lea20cbc22012-01-09 22:01:43 +0000316 // Ignore changes that are not online/offline transitions; e.g.
317 // switching between wired and wireless. TimeToDrop measures
318 // time online regardless of how we are connected.
319 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
320 return;
321
322 if (service == NULL) {
323 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
324 SendToUMA(kMetricTimeToDropSeconds,
325 elapsed_seconds.InSeconds(),
326 kMetricTimeToDropSecondsMin,
327 kMetricTimeToDropSecondsMax,
328 kTimerHistogramNumBuckets);
329 } else {
330 time_to_drop_timer_->Start();
331 }
332
333 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000334}
335
336void Metrics::NotifyServiceStateChanged(const Service *service,
337 Service::ConnectState new_state) {
338 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
339 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700340 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000341 DCHECK(false);
342 return;
343 }
344 ServiceMetrics *service_metrics = it->second.get();
345 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
346
347 if (new_state == Service::kStateFailure)
348 SendServiceFailure(service);
349
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200350 if (collect_bootstats_) {
351 bootstat_log(
352 StringPrintf("network-%s-%s",
353 Technology::NameFromIdentifier(
354 service->technology()).c_str(),
355 service->GetStateString().c_str()).c_str());
356 }
357
Paul Stewart20088d82012-02-16 06:58:55 -0800358 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000359 return;
360
Thieu Leb84ba342012-03-02 15:15:19 -0800361 base::TimeDelta time_resume_to_ready;
362 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
363 time_resume_to_ready_timer_->Reset();
364 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000365}
366
367string Metrics::GetFullMetricName(const char *metric_name,
368 Technology::Identifier technology_id) {
369 string technology = Technology::NameFromIdentifier(technology_id);
370 technology[0] = base::ToUpperASCII(technology[0]);
371 return base::StringPrintf(metric_name, technology.c_str());
372}
373
Thieu Le67370f62012-02-14 23:01:42 +0000374void Metrics::NotifyServiceDisconnect(const Service *service) {
375 Technology::Identifier technology = service->technology();
376 string histogram = GetFullMetricName(kMetricDisconnect, technology);
377 SendToUMA(histogram,
378 service->explicitly_disconnected(),
379 kMetricDisconnectMin,
380 kMetricDisconnectMax,
381 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000382}
383
Thieu Leb84ba342012-03-02 15:15:19 -0800384void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
385 if (new_state == PowerManagerProxyDelegate::kOn) {
386 time_resume_to_ready_timer_->Start();
387 } else {
388 time_resume_to_ready_timer_->Reset();
389 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000390}
391
Paul Stewartff845fc2012-08-07 07:28:44 -0700392void Metrics::NotifyLinkMonitorFailure(
393 Technology::Identifier technology, LinkMonitorFailure failure) {
394 string histogram = GetFullMetricName(kMetricLinkMonitorFailure,
395 technology);
396 SendEnumToUMA(histogram, failure, kLinkMonitorFailureMax);
397}
398
399void Metrics::NotifyLinkMonitorResponseTimeSampleAdded(
400 Technology::Identifier technology,
401 unsigned int response_time_milliseconds) {
402 string histogram = GetFullMetricName(kMetricLinkMonitorResponseTimeSample,
403 technology);
404 SendToUMA(histogram,
405 response_time_milliseconds,
406 kMetricLinkMonitorResponseTimeSampleMin,
407 kMetricLinkMonitorResponseTimeSampleMax,
408 kMetricLinkMonitorResponseTimeSampleNumBuckets);
409}
410
Thieu Le48e6d6d2011-12-06 00:40:27 +0000411bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
412 return library_->SendEnumToUMA(name, sample, max);
413}
414
Thieu Lea20cbc22012-01-09 22:01:43 +0000415bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
416 int num_buckets) {
417 return library_->SendToUMA(name, sample, min, max, num_buckets);
418}
419
Thieu Le48e6d6d2011-12-06 00:40:27 +0000420void Metrics::InitializeCommonServiceMetrics(const Service *service) {
421 Technology::Identifier technology = service->technology();
422 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
423 technology);
424 AddServiceStateTransitionTimer(
425 service,
426 histogram,
427 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800428 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000429 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
430 AddServiceStateTransitionTimer(
431 service,
432 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800433 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000434 Service::kStatePortal);
435 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
436 AddServiceStateTransitionTimer(
437 service,
438 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800439 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000440 Service::kStateOnline);
441}
442
443void Metrics::UpdateServiceStateTransitionMetrics(
444 ServiceMetrics *service_metrics,
445 Service::ConnectState new_state) {
446 TimerReportersList::iterator it;
447 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
448 for (it = start_timers.begin(); it != start_timers.end(); ++it)
449 (*it)->Start();
450
451 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
452 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
453 (*it)->Stop();
454 (*it)->ReportMilliseconds();
455 }
456}
457
458void Metrics::SendServiceFailure(const Service *service) {
459 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
460 service->failure(),
461 kMetricNetworkServiceErrorsMax);
462}
463
464void Metrics::set_library(MetricsLibraryInterface *library) {
465 chromeos_metrics::TimerReporter::set_metrics_lib(library);
466 library_ = library;
467}
468
469} // namespace shill