shill: cellular: Track auto-connect metrics.

Network.Shill.Cellular.AutoConnectTries and
Network.Shill.Cellular.AutoConnectTotalTime.

BUG=chromium-os:38406
TEST=Connect modem via auto-connect and check chrome://histograms

Change-Id: I79c2db2b8f4897779275675c9c0fb6278298d462
Reviewed-on: https://gerrit.chromium.org/gerrit/42361
Commit-Queue: Thieu Le <thieule@chromium.org>
Reviewed-by: Thieu Le <thieule@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 6a685a5..6189942 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -447,8 +447,11 @@
                            weak_ptr_factory_.GetWeakPtr());
   OnConnecting();
   capability_->Connect(properties, error, cb);
-  if (error->IsSuccess())
-    metrics()->NotifyDeviceConnectStarted(interface_index());
+  if (!error->IsSuccess())
+    return;
+
+  bool is_auto_connecting = service_.get() && service_->is_auto_connecting();
+  metrics()->NotifyDeviceConnectStarted(interface_index(), is_auto_connecting);
 }
 
 // Note that there's no ResultCallback argument to this,
diff --git a/cellular_service.cc b/cellular_service.cc
index 24bc7b3..c916b9e 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -93,7 +93,8 @@
     : Service(control_interface, dispatcher, metrics, manager,
               Technology::kCellular),
       activate_over_non_cellular_network_(false),
-      cellular_(device) {
+      cellular_(device),
+      is_auto_connecting_(false) {
   set_connectable(true);
   PropertyStore *store = this->mutable_store();
   store->RegisterConstBool(kActivateOverNonCellularNetworkProperty,
@@ -275,6 +276,12 @@
     storage->DeleteKey(storage_group, key);
 }
 
+void CellularService::AutoConnect() {
+  is_auto_connecting_ = true;
+  Service::AutoConnect();
+  is_auto_connecting_ = false;
+}
+
 void CellularService::Connect(Error *error) {
   Service::Connect(error);
   cellular_->Connect(error);
diff --git a/cellular_service.h b/cellular_service.h
index 8af31e1..e665e56 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -61,6 +61,7 @@
   virtual ~CellularService();
 
   // Inherited from Service.
+  virtual void AutoConnect();
   virtual void Connect(Error *error);
   virtual void Disconnect(Error *error);
   virtual void ActivateCellularModem(const std::string &carrier,
@@ -95,6 +96,10 @@
   void SetRoamingState(const std::string &state);
   const std::string &roaming_state() const { return roaming_state_; }
 
+  bool is_auto_connecting() const {
+    return is_auto_connecting_;
+  }
+
   // Overrides Load and Save from parent Service class.  We will call
   // the parent method.
   virtual bool Load(StoreInterface *storage);
@@ -168,6 +173,12 @@
 
   CellularRefPtr cellular_;
 
+  // Flag indicating that a connect request is an auto-connect request.
+  // Note: Since Connect() is asynchronous, this flag is only set during the
+  // call to Connect().  It does not remain set while the async request is
+  // in flight.
+  bool is_auto_connecting_;
+
   DISALLOW_COPY_AND_ASSIGN(CellularService);
 };
 
diff --git a/metrics.cc b/metrics.cc
index abd5c7a..5220a25 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -198,6 +198,17 @@
 const int Metrics::kMetricCellularSignalStrengthBeforeDropMax = 100;
 const int Metrics::kMetricCellularSignalStrengthBeforeDropMin = 0;
 const int Metrics::kMetricCellularSignalStrengthBeforeDropNumBuckets = 10;
+const char Metrics::kMetricCellularAutoConnectTries[] =
+    "Network.Shill.Cellular.AutoConnectTries";
+const int Metrics::kMetricCellularAutoConnectTriesMax = 20;
+const int Metrics::kMetricCellularAutoConnectTriesMin = 1;
+const int Metrics::kMetricCellularAutoConnectTriesNumBuckets = 20;
+const char Metrics::kMetricCellularAutoConnectTotalTime[] =
+    "Network.Shill.Cellular.AutoConnectTotalTime";
+const int Metrics::kMetricCellularAutoConnectTotalTimeMax =
+    60 * 1000;  // 60 seconds
+const int Metrics::kMetricCellularAutoConnectTotalTimeMin = 0;
+const int Metrics::kMetricCellularAutoConnectTotalTimeNumBuckets = 60;
 
 
 Metrics::Metrics()
@@ -649,6 +660,13 @@
           kMetricTimeToConnectMillisecondsMin,
           kMetricTimeToConnectMillisecondsMax,
           kMetricTimeToConnectMillisecondsNumBuckets));
+  device_metrics->auto_connect_tries = 0;
+  device_metrics->auto_connect_timer.reset(
+      new chromeos_metrics::TimerReporter(
+          kMetricCellularAutoConnectTotalTime,
+          kMetricCellularAutoConnectTotalTimeMin,
+          kMetricCellularAutoConnectTotalTimeMax,
+          kMetricCellularAutoConnectTotalTimeNumBuckets));
 }
 
 bool Metrics::IsDeviceRegistered(int interface_index,
@@ -732,11 +750,20 @@
     device_metrics->scan_timer->ReportMilliseconds();
 }
 
-void Metrics::NotifyDeviceConnectStarted(int interface_index) {
+void Metrics::NotifyDeviceConnectStarted(int interface_index,
+                                         bool is_auto_connecting) {
   DeviceMetrics *device_metrics = GetDeviceMetrics(interface_index);
   if (device_metrics == NULL)
     return;
   device_metrics->connect_timer->Start();
+
+  if (is_auto_connecting) {
+    device_metrics->auto_connect_tries++;
+    if (device_metrics->auto_connect_tries == 1)
+      device_metrics->auto_connect_timer->Start();
+  } else {
+    AutoConnectMetricsReset(device_metrics);
+  }
 }
 
 void Metrics::NotifyDeviceConnectFinished(int interface_index) {
@@ -745,6 +772,21 @@
     return;
   device_metrics->connect_timer->Stop();
   device_metrics->connect_timer->ReportMilliseconds();
+
+  if (device_metrics->auto_connect_tries > 0) {
+    device_metrics->auto_connect_timer->Stop();
+    base::TimeDelta elapsed_time;
+    device_metrics->auto_connect_timer->GetElapsedTime(&elapsed_time);
+    if (elapsed_time.InMilliseconds() > kMetricCellularAutoConnectTotalTimeMax)
+      return;
+    device_metrics->auto_connect_timer->ReportMilliseconds();
+    SendToUMA(kMetricCellularAutoConnectTries,
+              device_metrics->auto_connect_tries,
+              kMetricCellularAutoConnectTriesMin,
+              kMetricCellularAutoConnectTriesMax,
+              kMetricCellularAutoConnectTriesNumBuckets);
+    AutoConnectMetricsReset(device_metrics);
+  }
 }
 
 void Metrics::NotifyCellularDeviceDrop(const string &network_technology,
@@ -845,6 +887,11 @@
   return it->second.get();
 }
 
+void Metrics::AutoConnectMetricsReset(DeviceMetrics *device_metrics) {
+  device_metrics->auto_connect_tries = 0;
+  device_metrics->auto_connect_timer->Reset();
+}
+
 void Metrics::set_library(MetricsLibraryInterface *library) {
   chromeos_metrics::TimerReporter::set_metrics_lib(library);
   library_ = library;
diff --git a/metrics.h b/metrics.h
index bd81924..57c1ce7 100644
--- a/metrics.h
+++ b/metrics.h
@@ -282,6 +282,14 @@
   static const int kMetricCellularSignalStrengthBeforeDropMax;
   static const int kMetricCellularSignalStrengthBeforeDropMin;
   static const int kMetricCellularSignalStrengthBeforeDropNumBuckets;
+  static const char kMetricCellularAutoConnectTries[];
+  static const int kMetricCellularAutoConnectTriesMax;
+  static const int kMetricCellularAutoConnectTriesMin;
+  static const int kMetricCellularAutoConnectTriesNumBuckets;
+  static const char kMetricCellularAutoConnectTotalTime[];
+  static const int kMetricCellularAutoConnectTotalTimeMax;
+  static const int kMetricCellularAutoConnectTotalTimeMin;
+  static const int kMetricCellularAutoConnectTotalTimeNumBuckets;
 
   Metrics();
   virtual ~Metrics();
@@ -390,7 +398,8 @@
   void NotifyDeviceScanFinished(int interface_index);
 
   // Notifies this object that a device has started the connect process.
-  void NotifyDeviceConnectStarted(int interface_index);
+  void NotifyDeviceConnectStarted(int interface_index,
+                                  bool is_auto_connecting);
 
   // Notifies this object that a device has completed the connect process.
   void NotifyDeviceConnectFinished(int interface_index);
@@ -447,6 +456,8 @@
     scoped_ptr<chromeos_metrics::TimerReporter> disable_timer;
     scoped_ptr<chromeos_metrics::TimerReporter> scan_timer;
     scoped_ptr<chromeos_metrics::TimerReporter> connect_timer;
+    int auto_connect_tries;
+    scoped_ptr<chromeos_metrics::TimerReporter> auto_connect_timer;
   };
   typedef std::map<const int, std::tr1::shared_ptr<DeviceMetrics> >
       DeviceMetricsLookupMap;
@@ -472,6 +483,7 @@
   void SendServiceFailure(const Service *service);
 
   DeviceMetrics *GetDeviceMetrics (int interface_index) const;
+  void AutoConnectMetricsReset(DeviceMetrics *device_metrics);
 
   // For unit test purposes.
   void set_library(MetricsLibraryInterface *library);
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index cf99050..ab8875f 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -343,7 +343,7 @@
                 Metrics::kMetricTimeToConnectMillisecondsNumBuckets));
   const int kInterfaceIndex = 1;
   metrics_.RegisterDevice(kInterfaceIndex, Technology::kCellular);
-  metrics_.NotifyDeviceConnectStarted(kInterfaceIndex);
+  metrics_.NotifyDeviceConnectStarted(kInterfaceIndex, false);
   metrics_.NotifyDeviceConnectFinished(kInterfaceIndex);
 }
 
@@ -417,6 +417,32 @@
   metrics_.NotifyDeviceScanFinished(kInterfaceIndex);
 }
 
+TEST_F(MetricsTest, CellularAutoConnect) {
+  EXPECT_CALL(library_,
+      SendToUMA("Network.Shill.Cellular.TimeToConnect",
+                Ge(0),
+                Metrics::kMetricTimeToConnectMillisecondsMin,
+                Metrics::kMetricTimeToConnectMillisecondsMax,
+                Metrics::kMetricTimeToConnectMillisecondsNumBuckets));
+  EXPECT_CALL(library_,
+      SendToUMA(Metrics::kMetricCellularAutoConnectTotalTime,
+                Ge(0),
+                Metrics::kMetricCellularAutoConnectTotalTimeMin,
+                Metrics::kMetricCellularAutoConnectTotalTimeMax,
+                Metrics::kMetricCellularAutoConnectTotalTimeNumBuckets));
+  EXPECT_CALL(library_,
+      SendToUMA(Metrics::kMetricCellularAutoConnectTries,
+                2,
+                Metrics::kMetricCellularAutoConnectTriesMin,
+                Metrics::kMetricCellularAutoConnectTriesMax,
+                Metrics::kMetricCellularAutoConnectTriesNumBuckets));
+  const int kInterfaceIndex = 1;
+  metrics_.RegisterDevice(kInterfaceIndex, Technology::kCellular);
+  metrics_.NotifyDeviceConnectStarted(kInterfaceIndex, true);
+  metrics_.NotifyDeviceConnectStarted(kInterfaceIndex, true);
+  metrics_.NotifyDeviceConnectFinished(kInterfaceIndex);
+}
+
 TEST_F(MetricsTest, CellularDrop) {
   const char *kUMATechnologyStrings[] = {
       flimflam::kNetworkTechnology1Xrtt,