blob: 725ef2102f9ab1532aad1e04587d2bde389d2547 [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/string_util.h>
8#include <base/stringprintf.h>
Thieu Lead1ec2c2012-01-05 23:39:48 +00009#include <chromeos/dbus/service_constants.h>
Darin Petkov58f0b6d2012-06-12 12:52:30 +020010#include <metrics/bootstat.h>
Thieu Le48e6d6d2011-12-06 00:40:27 +000011
Paul Stewartff845fc2012-08-07 07:28:44 -070012#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070013#include "shill/logging.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000014#include "shill/wifi_service.h"
15
16using std::string;
17using std::tr1::shared_ptr;
18
19namespace shill {
20
Thieu Le48e6d6d2011-12-06 00:40:27 +000021// static
Thieu Lec31e6f92012-08-03 13:08:58 -070022// Our disconnect enumeration values are 0 (System Disconnect) and
23// 1 (User Disconnect), see histograms.xml, but Chrome needs a minimum
24// enum value of 1 and the minimum number of buckets needs to be 3 (see
25// histogram.h). Instead of remapping System Disconnect to 1 and
26// User Disconnect to 2, we can just leave the enumerated values as-is
27// because Chrome implicitly creates a [0-1) bucket for us. Using Min=1,
28// Max=2 and NumBuckets=3 gives us the following three buckets:
29// [0-1), [1-2), [2-INT_MAX). We end up with an extra bucket [2-INT_MAX)
30// that we can safely ignore.
Thieu Le67370f62012-02-14 23:01:42 +000031const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
Thieu Lec31e6f92012-08-03 13:08:58 -070032const int Metrics::kMetricDisconnectMax = 2;
33const int Metrics::kMetricDisconnectMin = 1;
34const int Metrics::kMetricDisconnectNumBuckets = 3;
Thieu Le67370f62012-02-14 23:01:42 +000035
Thieu Le48e6d6d2011-12-06 00:40:27 +000036const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
37const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000038const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
39const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
40const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
41const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000042const char Metrics::kMetricNetworkServiceErrors[] =
43 "Network.Shill.ServiceErrors";
44const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000045
46const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
47const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
48const int Metrics::kMetricTimeOnlineSecondsMin = 1;
49
50const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
51const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
52const int Metrics::kMetricTimeToDropSecondsMin = 1;
53
Thieu Leb84ba342012-03-02 15:15:19 -080054const char Metrics::kMetricTimeResumeToReadyMilliseconds[] =
55 "Network.Shill.%s.TimeResumeToReady";
Thieu Le48e6d6d2011-12-06 00:40:27 +000056const char Metrics::kMetricTimeToConfigMilliseconds[] =
57 "Network.Shill.%s.TimeToConfig";
58const char Metrics::kMetricTimeToJoinMilliseconds[] =
59 "Network.Shill.%s.TimeToJoin";
60const char Metrics::kMetricTimeToOnlineMilliseconds[] =
61 "Network.Shill.%s.TimeToOnline";
62const char Metrics::kMetricTimeToPortalMilliseconds[] =
63 "Network.Shill.%s.TimeToPortal";
Thieu Lea20cbc22012-01-09 22:01:43 +000064const int Metrics::kTimerHistogramMillisecondsMax = 45 * 1000;
65const int Metrics::kTimerHistogramMillisecondsMin = 1;
Thieu Le48e6d6d2011-12-06 00:40:27 +000066const int Metrics::kTimerHistogramNumBuckets = 50;
67
Thieu Le85e050b2012-03-13 15:04:38 -070068const char Metrics::kMetricPortalAttempts[] =
69 "Network.Shill.%s.PortalAttempts";
70const int Metrics::kMetricPortalAttemptsMax =
71 PortalDetector::kMaxRequestAttempts;
72const int Metrics::kMetricPortalAttemptsMin = 1;
73const int Metrics::kMetricPortalAttemptsNumBuckets =
74 Metrics::kMetricPortalAttemptsMax;
75
76const char Metrics::kMetricPortalAttemptsToOnline[] =
77 "Network.Shill.%s.PortalAttemptsToOnline";
78const int Metrics::kMetricPortalAttemptsToOnlineMax = 100;
79const int Metrics::kMetricPortalAttemptsToOnlineMin = 1;
80const int Metrics::kMetricPortalAttemptsToOnlineNumBuckets = 10;
81
82const char Metrics::kMetricPortalResult[] = "Network.Shill.%s.PortalResult";
83
Thieu Le48e6d6d2011-12-06 00:40:27 +000084// static
85const uint16 Metrics::kWiFiBandwidth5MHz = 5;
86const uint16 Metrics::kWiFiBandwidth20MHz = 20;
87const uint16 Metrics::kWiFiFrequency2412 = 2412;
88const uint16 Metrics::kWiFiFrequency2472 = 2472;
89const uint16 Metrics::kWiFiFrequency2484 = 2484;
90const uint16 Metrics::kWiFiFrequency5170 = 5170;
91const uint16 Metrics::kWiFiFrequency5180 = 5180;
92const uint16 Metrics::kWiFiFrequency5230 = 5230;
93const uint16 Metrics::kWiFiFrequency5240 = 5240;
94const uint16 Metrics::kWiFiFrequency5320 = 5320;
95const uint16 Metrics::kWiFiFrequency5500 = 5500;
96const uint16 Metrics::kWiFiFrequency5700 = 5700;
97const uint16 Metrics::kWiFiFrequency5745 = 5745;
98const uint16 Metrics::kWiFiFrequency5825 = 5825;
99
Thieu Leb84ba342012-03-02 15:15:19 -0800100// static
101const char Metrics::kMetricPowerManagerKey[] = "metrics";
102
Paul Stewartff845fc2012-08-07 07:28:44 -0700103// static
104const char Metrics::kMetricLinkMonitorFailure[] =
105 "Network.Shill.%s.LinkMonitorFailure";
106const char Metrics::kMetricLinkMonitorResponseTimeSample[] =
107 "Network.Shill.%s.LinkMonitorResponseTimeSample";
108const int Metrics::kMetricLinkMonitorResponseTimeSampleMin = 0;
109const int Metrics::kMetricLinkMonitorResponseTimeSampleMax =
110 LinkMonitor::kTestPeriodMilliseconds;
111const int Metrics::kMetricLinkMonitorResponseTimeSampleNumBuckets = 50;
112
113
Thieu Lea20cbc22012-01-09 22:01:43 +0000114Metrics::Metrics()
115 : library_(&metrics_library_),
116 last_default_technology_(Technology::kUnknown),
117 was_online_(false),
118 time_online_timer_(new chromeos_metrics::Timer),
Thieu Leb84ba342012-03-02 15:15:19 -0800119 time_to_drop_timer_(new chromeos_metrics::Timer),
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200120 time_resume_to_ready_timer_(new chromeos_metrics::Timer),
121 collect_bootstats_(true) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000122 metrics_library_.Init();
123 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
124}
125
126Metrics::~Metrics() {}
127
128// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000129Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
130 WiFiChannel channel = kWiFiChannelUndef;
131 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
132 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
133 channel = static_cast<WiFiChannel>(
134 kWiFiChannel2412 +
135 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
136 } else if (frequency == kWiFiFrequency2484) {
137 channel = kWiFiChannel2484;
138 } else if (kWiFiFrequency5170 <= frequency &&
139 frequency <= kWiFiFrequency5230) {
140 if ((frequency % kWiFiBandwidth20MHz) == 0)
141 channel = static_cast<WiFiChannel>(
142 kWiFiChannel5180 +
143 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
144 if ((frequency % kWiFiBandwidth20MHz) == 10)
145 channel = static_cast<WiFiChannel>(
146 kWiFiChannel5170 +
147 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
148 } else if (kWiFiFrequency5240 <= frequency &&
149 frequency <= kWiFiFrequency5320) {
150 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
151 channel = static_cast<WiFiChannel>(
152 kWiFiChannel5180 +
153 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
154 } else if (kWiFiFrequency5500 <= frequency &&
155 frequency <= kWiFiFrequency5700) {
156 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
157 channel = static_cast<WiFiChannel>(
158 kWiFiChannel5500 +
159 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
160 } else if (kWiFiFrequency5745 <= frequency &&
161 frequency <= kWiFiFrequency5825) {
162 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
163 channel = static_cast<WiFiChannel>(
164 kWiFiChannel5745 +
165 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
166 }
167 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
168
169 if (channel == kWiFiChannelUndef)
170 LOG(WARNING) << "no mapping for frequency " << frequency;
171 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700172 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000173
174 return channel;
175}
176
Thieu Lead1ec2c2012-01-05 23:39:48 +0000177// static
178Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
179 const std::string &security) {
180 if (security == flimflam::kSecurityNone) {
181 return kWiFiSecurityNone;
182 } else if (security == flimflam::kSecurityWep) {
183 return kWiFiSecurityWep;
184 } else if (security == flimflam::kSecurityWpa) {
185 return kWiFiSecurityWpa;
186 } else if (security == flimflam::kSecurityRsn) {
187 return kWiFiSecurityRsn;
188 } else if (security == flimflam::kSecurity8021x) {
189 return kWiFiSecurity8021x;
190 } else if (security == flimflam::kSecurityPsk) {
191 return kWiFiSecurityPsk;
192 } else {
193 return kWiFiSecurityUnknown;
194 }
195}
196
Thieu Le85e050b2012-03-13 15:04:38 -0700197// static
198Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
199 const PortalDetector::Result &result) {
200 DCHECK(result.final);
201 PortalResult retval = kPortalResultUnknown;
202 // The only time we should end a successful portal detection is when we're
203 // in the Content phase. If we end with kStatusSuccess in any other phase,
204 // then this indicates that something bad has happened.
205 switch (result.phase) {
206 case PortalDetector::kPhaseDNS:
207 if (result.status == PortalDetector::kStatusFailure)
208 retval = kPortalResultDNSFailure;
209 else if (result.status == PortalDetector::kStatusTimeout)
210 retval = kPortalResultDNSTimeout;
211 else
212 LOG(DFATAL) << __func__ << ": Final result status " << result.status
213 << " is not allowed in the DNS phase";
214 break;
215
216 case PortalDetector::kPhaseConnection:
217 if (result.status == PortalDetector::kStatusFailure)
218 retval = kPortalResultConnectionFailure;
219 else if (result.status == PortalDetector::kStatusTimeout)
220 retval = kPortalResultConnectionTimeout;
221 else
222 LOG(DFATAL) << __func__ << ": Final result status " << result.status
223 << " is not allowed in the Connection phase";
224 break;
225
226 case PortalDetector::kPhaseHTTP:
227 if (result.status == PortalDetector::kStatusFailure)
228 retval = kPortalResultHTTPFailure;
229 else if (result.status == PortalDetector::kStatusTimeout)
230 retval = kPortalResultHTTPTimeout;
231 else
232 LOG(DFATAL) << __func__ << ": Final result status " << result.status
233 << " is not allowed in the HTTP phase";
234 break;
235
236 case PortalDetector::kPhaseContent:
237 if (result.status == PortalDetector::kStatusSuccess)
238 retval = kPortalResultSuccess;
239 else if (result.status == PortalDetector::kStatusFailure)
240 retval = kPortalResultContentFailure;
241 else if (result.status == PortalDetector::kStatusTimeout)
242 retval = kPortalResultContentTimeout;
243 else
244 LOG(DFATAL) << __func__ << ": Final result status " << result.status
245 << " is not allowed in the Content phase";
246 break;
247
248 case PortalDetector::kPhaseUnknown:
249 retval = kPortalResultUnknown;
250 break;
251
252 default:
253 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
254 break;
255 }
256
257 return retval;
258}
259
Thieu Le48e6d6d2011-12-06 00:40:27 +0000260void Metrics::RegisterService(const Service *service) {
261 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
262 services_metrics_[service] = service_metrics;
263 service_metrics->service = service;
264 InitializeCommonServiceMetrics(service);
265 service->InitializeCustomMetrics();
266}
267
268void Metrics::DeregisterService(const Service *service) {
269 services_metrics_.erase(service);
270}
271
272void Metrics::AddServiceStateTransitionTimer(
273 const Service *service,
274 const string &histogram_name,
275 Service::ConnectState start_state,
276 Service::ConnectState stop_state) {
277 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
278 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700279 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000280 DCHECK(false);
281 return;
282 }
283 ServiceMetrics *service_metrics = it->second.get();
284 CHECK(start_state < stop_state);
285 chromeos_metrics::TimerReporter *timer =
286 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000287 kTimerHistogramMillisecondsMin,
288 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000289 kTimerHistogramNumBuckets);
290 service_metrics->timers.push_back(timer); // passes ownership.
291 service_metrics->start_on_state[start_state].push_back(timer);
292 service_metrics->stop_on_state[stop_state].push_back(timer);
293}
294
Thieu Lea20cbc22012-01-09 22:01:43 +0000295void Metrics::NotifyDefaultServiceChanged(const Service *service) {
296 base::TimeDelta elapsed_seconds;
297
298 Technology::Identifier technology = (service) ? service->technology() :
299 Technology::kUnknown;
300 if (technology != last_default_technology_) {
301 if (last_default_technology_ != Technology::kUnknown) {
302 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
303 last_default_technology_);
304 time_online_timer_->GetElapsedTime(&elapsed_seconds);
305 SendToUMA(histogram,
306 elapsed_seconds.InSeconds(),
307 kMetricTimeOnlineSecondsMin,
308 kMetricTimeOnlineSecondsMax,
309 kTimerHistogramNumBuckets);
310 }
311 last_default_technology_ = technology;
312 time_online_timer_->Start();
313 }
314
Thieu Lea20cbc22012-01-09 22:01:43 +0000315 // Ignore changes that are not online/offline transitions; e.g.
316 // switching between wired and wireless. TimeToDrop measures
317 // time online regardless of how we are connected.
318 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
319 return;
320
321 if (service == NULL) {
322 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
323 SendToUMA(kMetricTimeToDropSeconds,
324 elapsed_seconds.InSeconds(),
325 kMetricTimeToDropSecondsMin,
326 kMetricTimeToDropSecondsMax,
327 kTimerHistogramNumBuckets);
328 } else {
329 time_to_drop_timer_->Start();
330 }
331
332 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000333}
334
335void Metrics::NotifyServiceStateChanged(const Service *service,
336 Service::ConnectState new_state) {
337 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
338 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700339 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000340 DCHECK(false);
341 return;
342 }
343 ServiceMetrics *service_metrics = it->second.get();
344 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
345
346 if (new_state == Service::kStateFailure)
347 SendServiceFailure(service);
348
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200349 if (collect_bootstats_) {
350 bootstat_log(
351 StringPrintf("network-%s-%s",
352 Technology::NameFromIdentifier(
353 service->technology()).c_str(),
354 service->GetStateString().c_str()).c_str());
355 }
356
Paul Stewart20088d82012-02-16 06:58:55 -0800357 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000358 return;
359
Thieu Leb84ba342012-03-02 15:15:19 -0800360 base::TimeDelta time_resume_to_ready;
361 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
362 time_resume_to_ready_timer_->Reset();
363 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000364}
365
366string Metrics::GetFullMetricName(const char *metric_name,
367 Technology::Identifier technology_id) {
368 string technology = Technology::NameFromIdentifier(technology_id);
369 technology[0] = base::ToUpperASCII(technology[0]);
370 return base::StringPrintf(metric_name, technology.c_str());
371}
372
Thieu Le67370f62012-02-14 23:01:42 +0000373void Metrics::NotifyServiceDisconnect(const Service *service) {
374 Technology::Identifier technology = service->technology();
375 string histogram = GetFullMetricName(kMetricDisconnect, technology);
376 SendToUMA(histogram,
377 service->explicitly_disconnected(),
378 kMetricDisconnectMin,
379 kMetricDisconnectMax,
380 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000381}
382
Thieu Leb84ba342012-03-02 15:15:19 -0800383void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
384 if (new_state == PowerManagerProxyDelegate::kOn) {
385 time_resume_to_ready_timer_->Start();
386 } else {
387 time_resume_to_ready_timer_->Reset();
388 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000389}
390
Paul Stewartff845fc2012-08-07 07:28:44 -0700391void Metrics::NotifyLinkMonitorFailure(
392 Technology::Identifier technology, LinkMonitorFailure failure) {
393 string histogram = GetFullMetricName(kMetricLinkMonitorFailure,
394 technology);
395 SendEnumToUMA(histogram, failure, kLinkMonitorFailureMax);
396}
397
398void Metrics::NotifyLinkMonitorResponseTimeSampleAdded(
399 Technology::Identifier technology,
400 unsigned int response_time_milliseconds) {
401 string histogram = GetFullMetricName(kMetricLinkMonitorResponseTimeSample,
402 technology);
403 SendToUMA(histogram,
404 response_time_milliseconds,
405 kMetricLinkMonitorResponseTimeSampleMin,
406 kMetricLinkMonitorResponseTimeSampleMax,
407 kMetricLinkMonitorResponseTimeSampleNumBuckets);
408}
409
Thieu Le48e6d6d2011-12-06 00:40:27 +0000410bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
411 return library_->SendEnumToUMA(name, sample, max);
412}
413
Thieu Lea20cbc22012-01-09 22:01:43 +0000414bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
415 int num_buckets) {
416 return library_->SendToUMA(name, sample, min, max, num_buckets);
417}
418
Thieu Le48e6d6d2011-12-06 00:40:27 +0000419void Metrics::InitializeCommonServiceMetrics(const Service *service) {
420 Technology::Identifier technology = service->technology();
421 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
422 technology);
423 AddServiceStateTransitionTimer(
424 service,
425 histogram,
426 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800427 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000428 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
429 AddServiceStateTransitionTimer(
430 service,
431 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800432 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000433 Service::kStatePortal);
434 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
435 AddServiceStateTransitionTimer(
436 service,
437 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800438 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000439 Service::kStateOnline);
440}
441
442void Metrics::UpdateServiceStateTransitionMetrics(
443 ServiceMetrics *service_metrics,
444 Service::ConnectState new_state) {
445 TimerReportersList::iterator it;
446 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
447 for (it = start_timers.begin(); it != start_timers.end(); ++it)
448 (*it)->Start();
449
450 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
451 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
452 (*it)->Stop();
453 (*it)->ReportMilliseconds();
454 }
455}
456
457void Metrics::SendServiceFailure(const Service *service) {
458 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
459 service->failure(),
460 kMetricNetworkServiceErrorsMax);
461}
462
463void Metrics::set_library(MetricsLibraryInterface *library) {
464 chromeos_metrics::TimerReporter::set_metrics_lib(library);
465 library_ = library;
466}
467
468} // namespace shill