shill: Add Network.Shill.Cellular.TimeToConnect metric.

BUG=chromium-os:38086
TEST=Connect modem and look for metric in chrome://histograms

Change-Id: Icd4746508095cc23e104af8d47b3e3a0a03c040f
Reviewed-on: https://gerrit.chromium.org/gerrit/42026
Reviewed-by: Arman Uguray <armansito@chromium.org>
Commit-Queue: Thieu Le <thieule@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 1f6d59e..cba1754 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -438,16 +438,20 @@
                            weak_ptr_factory_.GetWeakPtr());
   OnConnecting();
   capability_->Connect(properties, error, cb);
+  if (error->IsSuccess())
+    metrics()->NotifyDeviceConnectStarted(interface_index());
 }
 
 // Note that there's no ResultCallback argument to this,
 // since Connect() isn't yet passed one.
 void Cellular::OnConnectReply(const Error &error) {
   SLOG(Cellular, 2) << __func__ << "(" << error << ")";
-  if (error.IsSuccess())
+  if (error.IsSuccess()) {
+    metrics()->NotifyDeviceConnectFinished(interface_index());
     OnConnected();
-  else
+  } else {
     OnConnectFailed(error);
+  }
 }
 
 void Cellular::OnConnecting() {
diff --git a/metrics.cc b/metrics.cc
index 0025305..c074499 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -53,6 +53,13 @@
 const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60;  // 8 hours
 const int Metrics::kMetricTimeOnlineSecondsMin = 1;
 
+const char Metrics::kMetricTimeToConnectMilliseconds[] =
+    "Network.Shill.%s.TimeToConnect";
+const int Metrics::kMetricTimeToConnectMillisecondsMax =
+    60 * 1000;  // 60 seconds
+const int Metrics::kMetricTimeToConnectMillisecondsMin = 1;
+const int Metrics::kMetricTimeToConnectMillisecondsNumBuckets = 60;
+
 const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
 const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60;  // 8 hours
 const int Metrics::kMetricTimeToDropSecondsMin = 1;
@@ -610,6 +617,14 @@
           kMetricTimeToDisableMillisecondsMin,
           kMetricTimeToDisableMillisecondsMax,
           kMetricTimeToDisableMillisecondsNumBuckets));
+  histogram = GetFullMetricName(kMetricTimeToConnectMilliseconds,
+                                technology);
+  device_metrics->connect_timer.reset(
+      new chromeos_metrics::TimerReporter(
+          histogram,
+          kMetricTimeToConnectMillisecondsMin,
+          kMetricTimeToConnectMillisecondsMax,
+          kMetricTimeToConnectMillisecondsNumBuckets));
 }
 
 bool Metrics::IsDeviceRegistered(int interface_index,
@@ -666,6 +681,21 @@
   device_metrics->disable_timer->ReportMilliseconds();
 }
 
+void Metrics::NotifyDeviceConnectStarted(int interface_index) {
+  DeviceMetrics *device_metrics = GetDeviceMetrics(interface_index);
+  if (device_metrics == NULL)
+    return;
+  device_metrics->connect_timer->Start();
+}
+
+void Metrics::NotifyDeviceConnectFinished(int interface_index) {
+  DeviceMetrics *device_metrics = GetDeviceMetrics(interface_index);
+  if (device_metrics == NULL)
+    return;
+  device_metrics->connect_timer->Stop();
+  device_metrics->connect_timer->ReportMilliseconds();
+}
+
 bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
   return library_->SendEnumToUMA(name, sample, max);
 }
diff --git a/metrics.h b/metrics.h
index 9775692..73985c2 100644
--- a/metrics.h
+++ b/metrics.h
@@ -179,6 +179,10 @@
   static const int kMetricTimeOnlineSecondsNumBuckets;
   static const char kMetricTimeResumeToReadyMilliseconds[];
   static const char kMetricTimeToConfigMilliseconds[];
+  static const char kMetricTimeToConnectMilliseconds[];
+  static const int kMetricTimeToConnectMillisecondsMax;
+  static const int kMetricTimeToConnectMillisecondsMin;
+  static const int kMetricTimeToConnectMillisecondsNumBuckets;
   static const char kMetricTimeToDropSeconds[];
   static const int kMetricTimeToDropSecondsMax;
   static const int kMetricTimeToDropSecondsMin;
@@ -354,6 +358,12 @@
   // Notifies this object that a device has completed the disable process.
   void NotifyDeviceDisableFinished(int interface_index);
 
+  // Notifies this object that a device has started the connect process.
+  void NotifyDeviceConnectStarted(int interface_index);
+
+  // Notifies this object that a device has completed the connect process.
+  void NotifyDeviceConnectFinished(int interface_index);
+
   // Sends linear histogram data to UMA.
   virtual bool SendEnumToUMA(const std::string &name, int sample, int max);
 
@@ -398,6 +408,7 @@
     scoped_ptr<chromeos_metrics::TimerReporter> initialization_timer;
     scoped_ptr<chromeos_metrics::TimerReporter> enable_timer;
     scoped_ptr<chromeos_metrics::TimerReporter> disable_timer;
+    scoped_ptr<chromeos_metrics::TimerReporter> connect_timer;
   };
   typedef std::map<const int, std::tr1::shared_ptr<DeviceMetrics> >
       DeviceMetricsLookupMap;
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index c1acc3e..abc020e 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -330,6 +330,19 @@
             Metrics::PortalDetectionResultToEnum(result));
 }
 
+TEST_F(MetricsTest, TimeToConnect) {
+  EXPECT_CALL(library_,
+      SendToUMA("Network.Shill.Cellular.TimeToConnect",
+                Ge(0),
+                Metrics::kMetricTimeToConnectMillisecondsMin,
+                Metrics::kMetricTimeToConnectMillisecondsMax,
+                Metrics::kMetricTimeToConnectMillisecondsNumBuckets));
+  const int kInterfaceIndex = 1;
+  metrics_.RegisterDevice(kInterfaceIndex, Technology::kCellular);
+  metrics_.NotifyDeviceConnectStarted(kInterfaceIndex);
+  metrics_.NotifyDeviceConnectFinished(kInterfaceIndex);
+}
+
 TEST_F(MetricsTest, TimeToDisable) {
   EXPECT_CALL(library_,
       SendToUMA("Network.Shill.Cellular.TimeToDisable",