shill: add metrics for wake on WiFi throttling

Add metric to log whether or not wake on WiFi is disabled
in the duration of a single suspend because of excessive
dark resume wakes.

BUG=chrome-os-partner:35518
TEST=Compile shill and run unit tests using the following commands:

  P2_TEST_FILTER="shill::*" FEATURES="test" USE="wake_on_wifi \
    clang asan" emerge-samus shill

  P2_TEST_FILTER="shill::*" FEATURES="test" USE="-wake_on_wifi \
    clang asan" emerge-samus shill

Change-Id: I2c6552d1f2d7b5c6857bf565f65a7c9ecb92b873
Reviewed-on: https://chromium-review.googlesource.com/243428
Tested-by: Samuel Tan <samueltan@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Samuel Tan <samueltan@chromium.org>
diff --git a/metrics.cc b/metrics.cc
index aca2273..1be0239 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -383,6 +383,10 @@
     "Network.Shill.WiFi.WiFiConnectionStatusAfterWake";
 
 // static
+const char Metrics::kMetricWakeOnWiFiThrottled[] =
+    "Network.Shill.WiFi.WakeOnWiFiThrottled";
+
+// static
 const char Metrics::kMetricUnreliableLinkSignalStrengthSuffix[] =
     "UnreliableLinkSignalStrength";
 const int Metrics::kMetricSerivceSignalStrengthMin = 0;
@@ -401,7 +405,8 @@
       time_suspend_actions_timer(new chromeos_metrics::Timer),
       time_dark_resume_actions_timer(new chromeos_metrics::Timer),
       collect_bootstats_(true),
-      num_scan_results_expected_in_dark_resume_(0) {
+      num_scan_results_expected_in_dark_resume_(0),
+      wake_on_wifi_throttled_(false) {
   metrics_library_.Init();
   chromeos_metrics::TimerReporter::set_metrics_lib(library_);
 }
@@ -802,6 +807,7 @@
   if (time_suspend_actions_timer->HasStarted())
     return;
   time_suspend_actions_timer->Start();
+  wake_on_wifi_throttled_ = false;
 }
 
 void Metrics::NotifySuspendActionsCompleted(bool success) {
@@ -1421,6 +1427,18 @@
   return library_->SendToUMA(name, sample, min, max, num_buckets);
 }
 
+void Metrics::NotifyWakeOnWiFiThrottled() {
+    wake_on_wifi_throttled_ = true;
+}
+
+void Metrics::NotifySuspendWithWakeOnWiFiEnabledDone() {
+  WakeOnWiFiThrottled throttled_result = wake_on_wifi_throttled_
+                                             ? kWakeOnWiFiThrottledTrue
+                                             : kWakeOnWiFiThrottledFalse;
+  SendEnumToUMA(kMetricWakeOnWiFiThrottled, throttled_result,
+                kWakeOnWiFiThrottledMax);
+}
+
 void Metrics::InitializeCommonServiceMetrics(const Service &service) {
   Technology::Identifier technology = service.technology();
   string histogram = GetFullMetricName(kMetricTimeToConfigMillisecondsSuffix,
diff --git a/metrics.h b/metrics.h
index 2346a36..50b9457 100644
--- a/metrics.h
+++ b/metrics.h
@@ -422,6 +422,12 @@
     kWakeOnWiFiFeaturesEnabledStateMax
   };
 
+  enum WakeOnWiFiThrottled {
+    kWakeOnWiFiThrottledFalse = 0,
+    kWakeOnWiFiThrottledTrue = 1,
+    kWakeOnWiFiThrottledMax
+  };
+
   static const char kMetricDisconnectSuffix[];
   static const int kMetricDisconnectMax;
   static const int kMetricDisconnectMin;
@@ -692,6 +698,9 @@
   // WiFi device connection status after waking from suspend.
   static const char kMetricWiFiConnectionStatusAfterWake[];
 
+  // Whether or not wake on WiFi was throttled during the last suspend.
+  static const char kMetricWakeOnWiFiThrottled[];
+
   explicit Metrics(EventDispatcher *dispatcher);
   virtual ~Metrics();
 
@@ -952,6 +961,14 @@
   virtual bool SendToUMA(const std::string &name, int sample, int min,
                          int max, int num_buckets);
 
+  // Notifies this object that wake on WiFi has been disabled because of
+  // excessive dark resume wakes.
+  virtual void NotifyWakeOnWiFiThrottled();
+
+  // Notifies this object that shill has resumed from a period of suspension
+  // where wake on WiFi functionality was enabled on the NIC.
+  virtual void NotifySuspendWithWakeOnWiFiEnabledDone();
+
  private:
   friend class MetricsTest;
   FRIEND_TEST(MetricsTest, CellularDropsPerHour);
@@ -965,6 +982,8 @@
   FRIEND_TEST(MetricsTest, TimeToScanIgnore);
   FRIEND_TEST(MetricsTest, WiFiServiceChannel);
   FRIEND_TEST(MetricsTest, WiFiServicePostReady);
+  FRIEND_TEST(MetricsTest, NotifySuspendWithWakeOnWiFiEnabledDone);
+  FRIEND_TEST(MetricsTest, NotifyWakeOnWiFiThrottled);
   FRIEND_TEST(WiFiMainTest, GetGeolocationObjects);
 
   typedef ScopedVector<chromeos_metrics::TimerReporter> TimerReporters;
@@ -1077,6 +1096,7 @@
   bool collect_bootstats_;
   DeviceMetricsLookupMap devices_metrics_;
   int num_scan_results_expected_in_dark_resume_;
+  bool wake_on_wifi_throttled_;
 
   DISALLOW_COPY_AND_ASSIGN(Metrics);
 };
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index bb67e5c..0f440c2 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -832,6 +832,30 @@
   metrics_.NotifyConnectedToServiceAfterWake(status);
 }
 
+TEST_F(MetricsTest, NotifyWakeOnWiFiThrottled) {
+  EXPECT_FALSE(metrics_.wake_on_wifi_throttled_);
+  metrics_.NotifyWakeOnWiFiThrottled();
+  EXPECT_TRUE(metrics_.wake_on_wifi_throttled_);
+}
+
+TEST_F(MetricsTest, NotifySuspendWithWakeOnWiFiEnabledDone) {
+  const Metrics::WakeOnWiFiThrottled result_true =
+      Metrics::kWakeOnWiFiThrottledTrue;
+  metrics_.wake_on_wifi_throttled_ = true;
+  EXPECT_CALL(library_,
+              SendEnumToUMA("Network.Shill.WiFi.WakeOnWiFiThrottled",
+                            result_true, Metrics::kWakeOnWiFiThrottledMax));
+  metrics_.NotifySuspendWithWakeOnWiFiEnabledDone();
+
+  const Metrics::WakeOnWiFiThrottled result_false =
+      Metrics::kWakeOnWiFiThrottledFalse;
+  metrics_.wake_on_wifi_throttled_ = false;
+  EXPECT_CALL(library_,
+              SendEnumToUMA("Network.Shill.WiFi.WakeOnWiFiThrottled",
+                            result_false, Metrics::kWakeOnWiFiThrottledMax));
+  metrics_.NotifySuspendWithWakeOnWiFiEnabledDone();
+}
+
 #ifndef NDEBUG
 
 typedef MetricsTest MetricsDeathTest;
diff --git a/mock_metrics.h b/mock_metrics.h
index 3ccf5e5..04f0a33 100644
--- a/mock_metrics.h
+++ b/mock_metrics.h
@@ -74,6 +74,8 @@
                void(VerifyWakeOnWiFiSettingsResult result));
   MOCK_METHOD1(NotifyConnectedToServiceAfterWake,
                void(WiFiConnectionStatusAfterWake status));
+  MOCK_METHOD0(NotifyWakeOnWiFiThrottled, void());
+  MOCK_METHOD0(NotifySuspendWithWakeOnWiFiEnabledDone, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockMetrics);
diff --git a/wifi/wake_on_wifi.cc b/wifi/wake_on_wifi.cc
index a091956..ac5d5d3 100644
--- a/wifi/wake_on_wifi.cc
+++ b/wifi/wake_on_wifi.cc
@@ -962,6 +962,7 @@
     // Unconditionally disable wake on WiFi on resume if these features
     // were enabled before the last suspend.
     DisableWakeOnWiFi();
+    metrics_->NotifySuspendWithWakeOnWiFiEnabledDone();
   }
 #endif  // DISABLE_WAKE_ON_WIFI
 }
@@ -998,6 +999,7 @@
     wake_to_scan_timer_.Stop();
     DisableWakeOnWiFi();
     dark_resumes_since_last_suspend_.Clear();
+    metrics_->NotifyWakeOnWiFiThrottled();
     return;
   }
   // Only set dark resume to true after checking if we need to disable wake on
diff --git a/wifi/wake_on_wifi_unittest.cc b/wifi/wake_on_wifi_unittest.cc
index 6eb55ea..db17fcf 100644
--- a/wifi/wake_on_wifi_unittest.cc
+++ b/wifi/wake_on_wifi_unittest.cc
@@ -1921,6 +1921,7 @@
   EXPECT_TRUE(DHCPLeaseRenewalTimerIsRunning());
   EXPECT_TRUE(WakeToScanTimerIsRunning());
   EXPECT_FALSE(GetDarkResumesSinceLastSuspend()->Empty());
+  EXPECT_CALL(metrics_, NotifyWakeOnWiFiThrottled());
   OnDarkResume(is_connected, has_service_configured_for_autoconnect);
   EXPECT_FALSE(SuspendActionsCallbackIsNull());
   EXPECT_FALSE(DHCPLeaseRenewalTimerIsRunning());
@@ -1954,12 +1955,14 @@
   GetWakeOnWiFiTriggers()->insert(WakeOnWiFi::kPattern);
   EXPECT_CALL(netlink_manager_,
               SendNl80211Message(IsDisableWakeOnWiFiMsg(), _, _, _)).Times(1);
+  EXPECT_CALL(metrics_, NotifySuspendWithWakeOnWiFiEnabledDone()).Times(1);
   OnAfterResume();
 
   // No wake no WiFi triggers supported, so do nothing.
   ClearWakeOnWiFiTriggersSupported();
   EXPECT_CALL(netlink_manager_,
               SendNl80211Message(IsDisableWakeOnWiFiMsg(), _, _, _)).Times(0);
+  EXPECT_CALL(metrics_, NotifySuspendWithWakeOnWiFiEnabledDone()).Times(0);
   OnAfterResume();
 
   // Wake on WiFi features disabled, so do nothing.
@@ -1967,6 +1970,7 @@
   DisableWakeOnWiFiFeatures();
   EXPECT_CALL(netlink_manager_,
               SendNl80211Message(IsDisableWakeOnWiFiMsg(), _, _, _)).Times(0);
+  EXPECT_CALL(metrics_, NotifySuspendWithWakeOnWiFiEnabledDone()).Times(0);
   OnAfterResume();
 
   // Both WakeOnWiFi triggers are empty and Wake on WiFi features are disabled,
@@ -1975,6 +1979,7 @@
   DisableWakeOnWiFiFeatures();
   EXPECT_CALL(netlink_manager_,
               SendNl80211Message(IsDisableWakeOnWiFiMsg(), _, _, _)).Times(0);
+  EXPECT_CALL(metrics_, NotifySuspendWithWakeOnWiFiEnabledDone()).Times(0);
   OnAfterResume();
 }
 
@@ -2168,6 +2173,7 @@
        WakeOnWiFiDisabled_OnAfterResume_DoesNothing) {
   DisableWakeOnWiFiFeatures();
   EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0);
+  EXPECT_CALL(metrics_, NotifySuspendWithWakeOnWiFiEnabledDone()).Times(0);
   OnAfterResume();
 }