shill: cellular: Add metric to track out-of-credits detection.

BUG=chromium:233452
TEST=Unit tests

Change-Id: Idcb5cabc44892a2ed5bd3c4fe8768fe734155e3c
Reviewed-on: https://gerrit.chromium.org/gerrit/48829
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 c051a59..d3f8122 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -371,6 +371,12 @@
     SLOG(Cellular, 2) << "Active probe determined possible out-of-credits "
                       << "scenario.";
     if (service().get()) {
+      Metrics::CellularOutOfCreditsReason reason =
+          (result == ConnectionHealthChecker::kResultCongestedTxQueue) ?
+              Metrics::kCellularOutOfCreditsReasonTxCongested :
+              Metrics::kCellularOutOfCreditsReasonElongatedTimeWait;
+      metrics()->NotifyCellularOutOfCredits(reason);
+
       service()->SetOutOfCredits(true);
       SLOG(Cellular, 2) << "Disconnecting due to out-of-credit scenario.";
       Error error;
diff --git a/cellular_service.cc b/cellular_service.cc
index cab433b..1d110a7 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -340,6 +340,8 @@
     } else {
       LOG(ERROR) <<
           "Out-Of-Credits detection: Marking service as out-of-credits";
+      metrics()->NotifyCellularOutOfCredits(
+          Metrics::kCellularOutOfCreditsReasonConnectDisconnectLoop);
       SetOutOfCredits(true);
       ResetOutOfCreditsState();
     }
diff --git a/metrics.cc b/metrics.cc
index 0edd193..f0ffd1d 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -208,6 +208,17 @@
     "Network.Shill.WiFi.ApDisconnectType";
 
 // static
+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;
 const char Metrics::kMetricCellularDrop[] =
     "Network.Shill.Cellular.Drop";
 const char Metrics::kMetricCellularDropsPerHour[] =
@@ -220,22 +231,13 @@
 // via SendUserActionToUMA.
 const char Metrics::kMetricCellularFailureReason[] =
     "Network.Shill.Cellular.FailureReason: ";
+const char Metrics::kMetricCellularOutOfCreditsReason[] =
+    "Network.Shill.Cellular.OutOfCreditsReason";
 const char Metrics::kMetricCellularSignalStrengthBeforeDrop[] =
     "Network.Shill.Cellular.SignalStrengthBeforeDrop";
 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;
 
 // static
 const char Metrics::kMetricCorruptedProfile[] =
@@ -937,6 +939,13 @@
       kMetricCellularFailureReason + error.message());
 }
 
+void Metrics::NotifyCellularOutOfCredits(
+    Metrics::CellularOutOfCreditsReason reason) {
+  SendEnumToUMA(kMetricCellularOutOfCreditsReason,
+                reason,
+                kCellularOutOfCreditsReasonMax);
+}
+
 void Metrics::NotifyCorruptedProfile() {
   SendEnumToUMA(kMetricCorruptedProfile,
                 kCorruptedProfile,
diff --git a/metrics.h b/metrics.h
index 614cf86..c4971f2 100644
--- a/metrics.h
+++ b/metrics.h
@@ -197,6 +197,13 @@
     kCellularDropTechnologyMax
   };
 
+  enum CellularOutOfCreditsReason {
+    kCellularOutOfCreditsReasonConnectDisconnectLoop = 0,
+    kCellularOutOfCreditsReasonTxCongested = 1,
+    kCellularOutOfCreditsReasonElongatedTimeWait = 2,
+    kCellularOutOfCreditsReasonMax
+  };
+
   enum CorruptedProfile {
     kCorruptedProfile = 1,
     kCorruptedProfileMax
@@ -344,16 +351,6 @@
   static const char kMetricServiceFixupEntries[];
 
   // Cellular specific statistics.
-  static const char kMetricCellularDrop[];
-  static const char kMetricCellularDropsPerHour[];
-  static const int kMetricCellularDropsPerHourMax;
-  static const int kMetricCellularDropsPerHourMin;
-  static const int kMetricCellularDropsPerHourNumBuckets;
-  static const char kMetricCellularFailureReason[];
-  static const char kMetricCellularSignalStrengthBeforeDrop[];
-  static const int kMetricCellularSignalStrengthBeforeDropMax;
-  static const int kMetricCellularSignalStrengthBeforeDropMin;
-  static const int kMetricCellularSignalStrengthBeforeDropNumBuckets;
   static const char kMetricCellularAutoConnectTries[];
   static const int kMetricCellularAutoConnectTriesMax;
   static const int kMetricCellularAutoConnectTriesMin;
@@ -362,6 +359,17 @@
   static const int kMetricCellularAutoConnectTotalTimeMax;
   static const int kMetricCellularAutoConnectTotalTimeMin;
   static const int kMetricCellularAutoConnectTotalTimeNumBuckets;
+  static const char kMetricCellularDrop[];
+  static const char kMetricCellularDropsPerHour[];
+  static const int kMetricCellularDropsPerHourMax;
+  static const int kMetricCellularDropsPerHourMin;
+  static const int kMetricCellularDropsPerHourNumBuckets;
+  static const char kMetricCellularFailureReason[];
+  static const char kMetricCellularOutOfCreditsReason[];
+  static const char kMetricCellularSignalStrengthBeforeDrop[];
+  static const int kMetricCellularSignalStrengthBeforeDropMax;
+  static const int kMetricCellularSignalStrengthBeforeDropMin;
+  static const int kMetricCellularSignalStrengthBeforeDropNumBuckets;
 
   // Profile statistics.
   static const char kMetricCorruptedProfile[];
@@ -510,6 +518,10 @@
   // Notifies this object about a cellular device failure code.
   void NotifyCellularDeviceFailure(const Error &error);
 
+  // Notifies this object that a cellular service has been marked as
+  // out-of-credits.
+  void NotifyCellularOutOfCredits(Metrics::CellularOutOfCreditsReason reason);
+
   // Notifies this object about a corrupted profile.
   virtual void NotifyCorruptedProfile();
 
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index 9c10f95..e37ab32 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -564,6 +564,31 @@
   metrics_.NotifyCellularDeviceFailure(error);
 }
 
+TEST_F(MetricsTest, CellularOutOfCreditsReason) {
+  EXPECT_CALL(library_,
+      SendEnumToUMA(Metrics::kMetricCellularOutOfCreditsReason,
+                    Metrics::kCellularOutOfCreditsReasonConnectDisconnectLoop,
+                    Metrics::kCellularOutOfCreditsReasonMax));
+  metrics_.NotifyCellularOutOfCredits(
+      Metrics::kCellularOutOfCreditsReasonConnectDisconnectLoop);
+  Mock::VerifyAndClearExpectations(&library_);
+
+  EXPECT_CALL(library_,
+      SendEnumToUMA(Metrics::kMetricCellularOutOfCreditsReason,
+                    Metrics::kCellularOutOfCreditsReasonTxCongested,
+                    Metrics::kCellularOutOfCreditsReasonMax));
+  metrics_.NotifyCellularOutOfCredits(
+      Metrics::kCellularOutOfCreditsReasonTxCongested);
+  Mock::VerifyAndClearExpectations(&library_);
+
+  EXPECT_CALL(library_,
+      SendEnumToUMA(Metrics::kMetricCellularOutOfCreditsReason,
+                    Metrics::kCellularOutOfCreditsReasonElongatedTimeWait,
+                    Metrics::kCellularOutOfCreditsReasonMax));
+  metrics_.NotifyCellularOutOfCredits(
+      Metrics::kCellularOutOfCreditsReasonElongatedTimeWait);
+}
+
 TEST_F(MetricsTest, CorruptedProfile) {
   EXPECT_CALL(library_, SendEnumToUMA(Metrics::kMetricCorruptedProfile,
                                       Metrics::kCorruptedProfile,