shill: Add Network.Shill.Cellular.TimeToDisable.

BUG=chromium-os:38086
TEST=Unit tests, disable modem and check chrome://histograms

Change-Id: I691af3a98eed014f601a140c81f52f0f7bf38a33
Reviewed-on: https://gerrit.chromium.org/gerrit/41894
Reviewed-by: Arman Uguray <armansito@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Commit-Queue: Thieu Le <thieule@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
diff --git a/cellular_capability_classic.cc b/cellular_capability_classic.cc
index d62508d..cdecbbd 100644
--- a/cellular_capability_classic.cc
+++ b/cellular_capability_classic.cc
@@ -125,6 +125,7 @@
 }
 
 void CellularCapabilityClassic::FinishDisable(const ResultCallback &callback) {
+  metrics()->NotifyDeviceDisableFinished(cellular()->interface_index());
   ReleaseProxies();
   callback.Run(Error());
 }
@@ -175,6 +176,7 @@
   SLOG(Cellular, 2) << __func__;
   CHECK(!callback.is_null());
   Error error;
+  metrics()->NotifyDeviceDisableStarted(cellular()->interface_index());
   proxy_->Enable(false, &error, callback, kTimeoutEnable);
   if (error.IsFailure())
       callback.Run(error);
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index 3c1f87d..2920719 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -312,6 +312,7 @@
 
 void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
   Error error;
+  metrics()->NotifyDeviceDisableStarted(cellular()->interface_index());
   modem_proxy_->Enable(
       false, &error,
       Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
@@ -325,8 +326,10 @@
     const ResultCallback &callback, const Error &error) {
   SLOG(Cellular, 2) << __func__;
 
-  if (error.IsSuccess())
+  if (error.IsSuccess()) {
+    metrics()->NotifyDeviceDisableFinished(cellular()->interface_index());
     ReleaseProxies();
+  }
   callback.Run(error);
 }
 
diff --git a/metrics.cc b/metrics.cc
index af803b7..ef9d118 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -57,6 +57,13 @@
 const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60;  // 8 hours
 const int Metrics::kMetricTimeToDropSecondsMin = 1;
 
+const char Metrics::kMetricTimeToDisableMilliseconds[] =
+    "Network.Shill.%s.TimeToDisable";
+const int Metrics::kMetricTimeToDisableMillisecondsMax =
+    60 * 1000;  // 60 seconds
+const int Metrics::kMetricTimeToDisableMillisecondsMin = 1;
+const int Metrics::kMetricTimeToDisableMillisecondsNumBuckets = 60;
+
 const char Metrics::kMetricTimeToEnableMilliseconds[] =
     "Network.Shill.%s.TimeToEnable";
 const int Metrics::kMetricTimeToEnableMillisecondsMax =
@@ -588,6 +595,14 @@
           kMetricTimeToEnableMillisecondsMin,
           kMetricTimeToEnableMillisecondsMax,
           kMetricTimeToEnableMillisecondsNumBuckets));
+  histogram = GetFullMetricName(kMetricTimeToDisableMilliseconds,
+                                technology);
+  device_metrics->disable_timer.reset(
+      new chromeos_metrics::TimerReporter(
+          histogram,
+          kMetricTimeToDisableMillisecondsMin,
+          kMetricTimeToDisableMillisecondsMax,
+          kMetricTimeToDisableMillisecondsNumBuckets));
 }
 
 void Metrics::DeregisterDevice(int interface_index) {
@@ -617,6 +632,21 @@
   device_metrics->enable_timer->ReportMilliseconds();
 }
 
+void Metrics::NotifyDeviceDisableStarted(int interface_index) {
+  DeviceMetrics *device_metrics = GetDeviceMetrics(interface_index);
+  if (device_metrics == NULL)
+    return;
+  device_metrics->disable_timer->Start();
+}
+
+void Metrics::NotifyDeviceDisableFinished(int interface_index) {
+  DeviceMetrics *device_metrics = GetDeviceMetrics(interface_index);
+  if (device_metrics == NULL)
+    return;
+  device_metrics->disable_timer->Stop();
+  device_metrics->disable_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 8ac0bfb..d189df1 100644
--- a/metrics.h
+++ b/metrics.h
@@ -176,6 +176,10 @@
   static const char kMetricTimeToDropSeconds[];
   static const int kMetricTimeToDropSecondsMax;
   static const int kMetricTimeToDropSecondsMin;
+  static const char kMetricTimeToDisableMilliseconds[];
+  static const int kMetricTimeToDisableMillisecondsMax;
+  static const int kMetricTimeToDisableMillisecondsMin;
+  static const int kMetricTimeToDisableMillisecondsNumBuckets;
   static const char kMetricTimeToEnableMilliseconds[];
   static const int kMetricTimeToEnableMillisecondsMax;
   static const int kMetricTimeToEnableMillisecondsMin;
@@ -331,6 +335,12 @@
   // Notifies this object that a device has completed the enable process.
   void NotifyDeviceEnableFinished(int interface_index);
 
+  // Notifies this object that a device has started the disable process.
+  void NotifyDeviceDisableStarted(int interface_index);
+
+  // Notifies this object that a device has completed the disable process.
+  void NotifyDeviceDisableFinished(int interface_index);
+
   // Sends linear histogram data to UMA.
   virtual bool SendEnumToUMA(const std::string &name, int sample, int max);
 
@@ -373,6 +383,7 @@
     DeviceMetrics() {}
     scoped_ptr<chromeos_metrics::TimerReporter> initialization_timer;
     scoped_ptr<chromeos_metrics::TimerReporter> enable_timer;
+    scoped_ptr<chromeos_metrics::TimerReporter> disable_timer;
   };
   typedef std::map<const int, std::tr1::shared_ptr<DeviceMetrics> >
       DeviceMetricsLookupMap;
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index 4ce35d5..c1acc3e 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -330,6 +330,19 @@
             Metrics::PortalDetectionResultToEnum(result));
 }
 
+TEST_F(MetricsTest, TimeToDisable) {
+  EXPECT_CALL(library_,
+      SendToUMA("Network.Shill.Cellular.TimeToDisable",
+                Ge(0),
+                Metrics::kMetricTimeToDisableMillisecondsMin,
+                Metrics::kMetricTimeToDisableMillisecondsMax,
+                Metrics::kMetricTimeToDisableMillisecondsNumBuckets));
+  const int kInterfaceIndex = 1;
+  metrics_.RegisterDevice(kInterfaceIndex, Technology::kCellular);
+  metrics_.NotifyDeviceDisableStarted(kInterfaceIndex);
+  metrics_.NotifyDeviceDisableFinished(kInterfaceIndex);
+}
+
 TEST_F(MetricsTest, TimeToEnable) {
   EXPECT_CALL(library_,
       SendToUMA("Network.Shill.Cellular.TimeToEnable",