cellular: Upload UMA stats for 3GPP delayed registration drop.

crbug.com/238820 ensures that small glitches in 3GPP bearer registration
do not cause connection drop. These glitches were originally observed
for E362 modem. Upload UMA stats to figure out if the workaround should
be restricted to devices with E362.

BUG=chromium:241231
TEST=(1) build and run unit-tests.
     (2) Check that the following increments the value of
         the histogram Network.Shill.Cellular.3GPPRegistrationDelayedDrop in
         chrome://histograms on DUT.
         - Connect to cellular network on DUT.
         - Move the DUT to an area with bad reception, so that cellular
           connection is dropped.
         - Check that the histogram value for '0' is incremented and value for
           '1' remains unchanged.
     (3) Check that the following increments the value of
         the histogram Network.Shill.Cellular.3GPPRegistrationDelayedDrop in
         chrome://histograms on DUT.
         - Connect to cellular network on DUT.
         - Move the DUT to an area with bad reception so that signal strength
           goes down to 0. Return to an area with good reception within 10
           seconds.
         - Ensure that the cellular connection is never dropped.
         - Check that the histogram values for '0' and '1' are incremented.

Change-Id: Ic24ca0293226cefd4d334aec48a331a8b1f9b401
Reviewed-on: https://gerrit.chromium.org/gerrit/51398
Reviewed-by: Thieu Le <thieule@chromium.org>
Commit-Queue: Prathmesh Prabhu <pprabhu@chromium.org>
Tested-by: Prathmesh Prabhu <pprabhu@chromium.org>
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index 2c674a8..1c00fc1 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -1863,15 +1863,16 @@
 
   // While the modem is connected, if the state changed from a registered state
   // to a non registered state, defer the state change by 15 seconds.
-  // TODO(pprabhu) [crbug.com/241231] Add UMA stat to determine whether this
-  // condition is specific to E362. If it is, guard the delayed update by E362
-  // specific flag.
   if (cellular()->modem_state() == Cellular::kModemStateConnected &&
       IsRegistered() && !IsRegisteredState(state)) {
     if (!registration_dropped_update_callback_.IsCancelled()) {
       LOG(WARNING) << "Modem reported consecutive 3GPP registration drops. "
                    << "Ignoring earlier notifications.";
       registration_dropped_update_callback_.Cancel();
+    } else {
+      // This is not a repeated post. So, count this instance of delayed drop
+      // posted.
+      modem_info()->metrics()->Notify3GPPRegistrationDelayedDropPosted();
     }
     SLOG(Cellular, 2) << "Posted deferred registration state update";
     registration_dropped_update_callback_.Reset(
@@ -1887,6 +1888,9 @@
     if (!registration_dropped_update_callback_.IsCancelled()) {
       SLOG(Cellular, 2) << "Cancelled a deferred registration state update";
       registration_dropped_update_callback_.Cancel();
+      // If we cancelled the callback here, it means we had flaky network for a
+      // small duration.
+      modem_info()->metrics()->Notify3GPPRegistrationDelayedDropCanceled();
     }
     Handle3GPPRegistrationChange(state, operator_code, operator_name);
   }
diff --git a/cellular_capability_universal_unittest.cc b/cellular_capability_universal_unittest.cc
index d4fb59a..d8d05ec 100644
--- a/cellular_capability_universal_unittest.cc
+++ b/cellular_capability_universal_unittest.cc
@@ -785,6 +785,11 @@
             capability_->registration_state_);
 
   // Home --> Searching --> Home should never see Searching.
+  EXPECT_CALL(*(modem_info_.mock_metrics()),
+      Notify3GPPRegistrationDelayedDropPosted());
+  EXPECT_CALL(*(modem_info_.mock_metrics()),
+      Notify3GPPRegistrationDelayedDropCanceled());
+
   capability_->On3GPPRegistrationChanged(
       MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
       home_provider,
@@ -806,8 +811,11 @@
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
             capability_->registration_state_);
+  Mock::VerifyAndClearExpectations(modem_info_.mock_metrics());
 
   // Home --> Searching --> wait till dispatch should see Searching
+  EXPECT_CALL(*(modem_info_.mock_metrics()),
+      Notify3GPPRegistrationDelayedDropPosted());
   capability_->On3GPPRegistrationChanged(
       MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
       home_provider,
@@ -823,10 +831,14 @@
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
             capability_->registration_state_);
+  Mock::VerifyAndClearExpectations(modem_info_.mock_metrics());
 
   // Home --> Searching --> Searching --> wait till dispatch should see
   // Searching *and* the first callback should be cancelled.
   EXPECT_CALL(*this, DummyCallback()).Times(0);
+  EXPECT_CALL(*(modem_info_.mock_metrics()),
+      Notify3GPPRegistrationDelayedDropPosted());
+
   capability_->On3GPPRegistrationChanged(
       MM_MODEM_3GPP_REGISTRATION_STATE_HOME,
       home_provider,
@@ -847,7 +859,6 @@
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING,
             capability_->registration_state_);
-
 }
 
 TEST_F(CellularCapabilityUniversalMainTest,
diff --git a/metrics.cc b/metrics.cc
index c3d1278..14a07ca 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -216,6 +216,8 @@
     "Network.Shill.WiFi.ApDisconnectType";
 
 // static
+const char Metrics::kMetricCellular3GPPRegistrationDelayedDrop[] =
+    "Network.Shill.Cellular.3GPPRegistrationDelayedDrop";
 const char Metrics::kMetricCellularAutoConnectTries[] =
     "Network.Shill.Cellular.AutoConnectTries";
 const int Metrics::kMetricCellularAutoConnectTriesMax = 20;
@@ -934,6 +936,18 @@
   device_metrics->scan_connect_timer->Reset();
 }
 
+void Metrics::Notify3GPPRegistrationDelayedDropPosted() {
+  SendEnumToUMA(kMetricCellular3GPPRegistrationDelayedDrop,
+                kCellular3GPPRegistrationDelayedDropPosted,
+                kCellular3GPPRegistrationDelayedDropMax);
+}
+
+void Metrics::Notify3GPPRegistrationDelayedDropCanceled() {
+  SendEnumToUMA(kMetricCellular3GPPRegistrationDelayedDrop,
+                kCellular3GPPRegistrationDelayedDropCanceled,
+                kCellular3GPPRegistrationDelayedDropMax);
+}
+
 void Metrics::NotifyCellularDeviceDrop(int interface_index,
                                        const string &network_technology,
                                        uint16 signal_strength) {
diff --git a/metrics.h b/metrics.h
index 330e0de..58a7eaf 100644
--- a/metrics.h
+++ b/metrics.h
@@ -195,6 +195,12 @@
     kTerminationActionReasonTerminate
   };
 
+  enum Cellular3GPPRegistrationDelayedDrop {
+    kCellular3GPPRegistrationDelayedDropPosted = 0,
+    kCellular3GPPRegistrationDelayedDropCanceled = 1,
+    kCellular3GPPRegistrationDelayedDropMax
+  };
+
   enum CellularDropTechnology {
     kCellularDropTechnology1Xrtt = 0,
     kCellularDropTechnologyEdge = 1,
@@ -367,6 +373,7 @@
   static const char kMetricServiceFixupEntries[];
 
   // Cellular specific statistics.
+  static const char kMetricCellular3GPPRegistrationDelayedDrop[];
   static const char kMetricCellularAutoConnectTries[];
   static const int kMetricCellularAutoConnectTriesMax;
   static const int kMetricCellularAutoConnectTriesMin;
@@ -539,6 +546,10 @@
                                 const std::string &network_technology,
                                 uint16 signal_strength);
 
+  // Notifies this object about 3GPP registration drop events.
+  virtual void Notify3GPPRegistrationDelayedDropPosted();
+  virtual void Notify3GPPRegistrationDelayedDropCanceled();
+
   // Notifies this object about a cellular device failure code.
   void NotifyCellularDeviceFailure(const Error &error);
 
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index a4af912..f6e38a8 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -547,6 +547,21 @@
   metrics_.NotifyDeviceScanFinished(kInterfaceIndex);
 }
 
+TEST_F(MetricsTest, Cellular3GPPRegistrationDelayedDropPosted) {
+  EXPECT_CALL(library_,
+      SendEnumToUMA(Metrics::kMetricCellular3GPPRegistrationDelayedDrop,
+                    Metrics::kCellular3GPPRegistrationDelayedDropPosted,
+                    Metrics::kCellular3GPPRegistrationDelayedDropMax));
+  metrics_.Notify3GPPRegistrationDelayedDropPosted();
+  Mock::VerifyAndClearExpectations(&library_);
+
+  EXPECT_CALL(library_,
+      SendEnumToUMA(Metrics::kMetricCellular3GPPRegistrationDelayedDrop,
+                    Metrics::kCellular3GPPRegistrationDelayedDropCanceled,
+                    Metrics::kCellular3GPPRegistrationDelayedDropMax));
+  metrics_.Notify3GPPRegistrationDelayedDropCanceled();
+}
+
 TEST_F(MetricsTest, CellularAutoConnect) {
   EXPECT_CALL(library_,
       SendToUMA("Network.Shill.Cellular.TimeToConnect",
diff --git a/mock_metrics.h b/mock_metrics.h
index f3ed3a3..19af650 100644
--- a/mock_metrics.h
+++ b/mock_metrics.h
@@ -28,6 +28,8 @@
   MOCK_METHOD1(NotifyDefaultServiceChanged, void(const Service *service));
   MOCK_METHOD2(NotifyServiceStateChanged,
                void(const Service *service, Service::ConnectState new_state));
+  MOCK_METHOD0(Notify3GPPRegistrationDelayedDropPosted, void());
+  MOCK_METHOD0(Notify3GPPRegistrationDelayedDropCanceled, void());
   MOCK_METHOD0(NotifyCorruptedProfile, void());
   MOCK_METHOD3(SendEnumToUMA, bool(const std::string &name, int sample,
                                    int max));