UMA Metric for reporting network problem while connected.

Added a UMA metric for reporting network problem while connected to
a network. Network problem is detected by TrafficMonitor. Max of one
network problem will be reported per connection.

BUG=chromium:374274
TEST=unit tests, manual
Manual Test:
1. Connect a chrome device an AP without internet access.
2. Wait for few minutes, then browse to "chrome://histograms", and verify
   there is a histogram for Network.Shill.Wifi.NetworkProblemDetected".

Change-Id: Iad019d147feebcd429445c687a7c37d8e1e281f9
Reviewed-on: https://chromium-review.googlesource.com/200469
Reviewed-by: Peter Qiu <zqiu@chromium.org>
Commit-Queue: Peter Qiu <zqiu@chromium.org>
Tested-by: Peter Qiu <zqiu@chromium.org>
diff --git a/active_passive_out_of_credits_detector.cc b/active_passive_out_of_credits_detector.cc
index 9a3b99e..ea02fd0 100644
--- a/active_passive_out_of_credits_detector.cc
+++ b/active_passive_out_of_credits_detector.cc
@@ -31,7 +31,7 @@
       traffic_monitor_(
           new TrafficMonitor(service->cellular(), dispatcher)) {
   ResetDetector();
-  traffic_monitor_->set_tcp_out_traffic_not_routed_callback(
+  traffic_monitor_->set_network_problem_detected_callback(
       Bind(&ActivePassiveOutOfCreditsDetector::OnNoNetworkRouting,
            weak_ptr_factory_.GetWeakPtr()));
 }
@@ -102,7 +102,7 @@
   traffic_monitor_->Stop();
 }
 
-void ActivePassiveOutOfCreditsDetector::OnNoNetworkRouting() {
+void ActivePassiveOutOfCreditsDetector::OnNoNetworkRouting(int reason) {
   SLOG(Cellular, 2) << "Service " << service()->friendly_name()
                     << ": Traffic Monitor detected network congestion.";
   SLOG(Cellular, 2) << "Requesting active probe for out-of-credit detection.";
diff --git a/active_passive_out_of_credits_detector.h b/active_passive_out_of_credits_detector.h
index 3843e52..b49b7fa 100644
--- a/active_passive_out_of_credits_detector.h
+++ b/active_passive_out_of_credits_detector.h
@@ -58,7 +58,7 @@
   void StopTrafficMonitor();
 
   // Responds to a TrafficMonitor no-network-routing failure.
-  void OnNoNetworkRouting();
+  void OnNoNetworkRouting(int reason);
 
   // Initializes and configures the connection health checker.
   void SetupConnectionHealthChecker();
diff --git a/active_passive_out_of_credits_detector_unittest.cc b/active_passive_out_of_credits_detector_unittest.cc
index b7dec85..de1e23c 100644
--- a/active_passive_out_of_credits_detector_unittest.cc
+++ b/active_passive_out_of_credits_detector_unittest.cc
@@ -282,7 +282,7 @@
                Unretained(this)));
   SetConnectionHealthChecker(health_checker);  // Passes ownership.
   EXPECT_CALL(*health_checker, Start());
-  out_of_credits_detector_->OnNoNetworkRouting();
+  out_of_credits_detector_->OnNoNetworkRouting(0);
   EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
   Mock::VerifyAndClearExpectations(health_checker);
 
@@ -291,7 +291,7 @@
   EXPECT_CALL(*health_checker, health_check_in_progress())
       .WillOnce(Return(true));
   EXPECT_CALL(*health_checker, Start()).Times(0);
-  out_of_credits_detector_->OnNoNetworkRouting();
+  out_of_credits_detector_->OnNoNetworkRouting(0);
 }
 
 TEST_F(ActivePassiveOutOfCreditsDetectorTest,
diff --git a/device.cc b/device.cc
index 3482747..b8a3a46 100644
--- a/device.cc
+++ b/device.cc
@@ -43,6 +43,7 @@
 #include "shill/store_interface.h"
 #include "shill/technology.h"
 #include "shill/tethering.h"
+#include "shill/traffic_monitor.h"
 
 using base::Bind;
 using base::FilePath;
@@ -566,6 +567,7 @@
     StartPortalDetection();
   }
   StartLinkMonitor();
+  StartTrafficMonitor();
   UpdateIPConfigsProperty();
 }
 
@@ -656,6 +658,7 @@
 
 void Device::DestroyConnection() {
   SLOG(Device, 2) << __func__ << " on " << link_name_;
+  StopTrafficMonitor();
   StopPortalDetection();
   StopLinkMonitor();
   if (selected_service_.get()) {
@@ -684,6 +687,7 @@
     // Just in case the Device subclass has not already done so, make
     // sure the previously selected service has its connection removed.
     selected_service_->SetConnection(NULL);
+    StopTrafficMonitor();
     StopLinkMonitor();
     StopPortalDetection();
   }
@@ -914,6 +918,65 @@
   fallback_dns_test_client_.reset();
 }
 
+void Device::set_traffic_monitor(TrafficMonitor *traffic_monitor) {
+  traffic_monitor_.reset(traffic_monitor);
+}
+
+bool Device::IsTrafficMonitorEnabled() const {
+  return false;
+}
+
+void Device::StartTrafficMonitor() {
+  // Return if traffic monitor is not enabled for this device.
+  if (!IsTrafficMonitorEnabled()) {
+    return;
+  }
+
+  SLOG(Device, 2) << "Device " << FriendlyName()
+                  << ": Traffic Monitor starting.";
+  if (!traffic_monitor_.get()) {
+    traffic_monitor_.reset(new TrafficMonitor(this, dispatcher_));
+    traffic_monitor_->set_network_problem_detected_callback(
+        Bind(&Device::OnEncounterNetworkProblem,
+             weak_ptr_factory_.GetWeakPtr()));
+  }
+  traffic_monitor_->Start();
+}
+
+void Device::StopTrafficMonitor() {
+  // Return if traffic monitor is not enabled for this device.
+  if (!IsTrafficMonitorEnabled()) {
+    return;
+  }
+
+  if (traffic_monitor_.get()) {
+    SLOG(Device, 2) << "Device " << FriendlyName()
+                    << ": Traffic Monitor stopping.";
+    traffic_monitor_->Stop();
+  }
+  traffic_monitor_.reset();
+}
+
+void Device::OnEncounterNetworkProblem(int reason) {
+  int metric_code;
+  switch (reason) {
+    case TrafficMonitor::kNetworkProblemCongestedTxQueue:
+      metric_code = Metrics::kNetworkProblemCongestedTCPTxQueue;
+      break;
+    case TrafficMonitor::kNetworkProblemDNSFailure:
+      metric_code = Metrics::kNetworkProblemDNSFailure;
+      break;
+    default:
+      LOG(ERROR) << "Invalid network problem code: " << reason;
+      return;
+  }
+
+  metrics()->NotifyNetworkProblemDetected(technology_, metric_code);
+  // Stop the traffic monitor, only report the first network problem detected
+  // on the connection for now.
+  StopTrafficMonitor();
+}
+
 void Device::SetServiceConnectedState(Service::ConnectState state) {
   DCHECK(selected_service_.get());
 
diff --git a/device.h b/device.h
index 34500aa..e1408db 100644
--- a/device.h
+++ b/device.h
@@ -436,6 +436,23 @@
   // Respond to a LinkMonitor gateway's MAC address found/change event.
   virtual void OnLinkMonitorGatewayChange();
 
+  // Returns true if traffic monitor is enabled on this device. The default
+  // implementation will return false, which can be overridden by a derived
+  // class.
+  virtual bool IsTrafficMonitorEnabled() const;
+
+  // Initiates traffic monitoring on the device if traffic monitor is enabled.
+  void StartTrafficMonitor();
+
+  // Stops traffic monitoring on the device if traffic monitor is enabled.
+  void StopTrafficMonitor();
+
+  // Called by the Traffic Monitor when it detects a network problem. Device
+  // subclasses that want to roam to a different network when encountering
+  // network problems can override this method in order to do so. The parent
+  // implementation handles the metric reporting of the network problem.
+  virtual void OnEncounterNetworkProblem(int reason);
+
   // Set the state of the selected service, with checks to make sure
   // the service is already in a connected state before doing so.
   void SetServiceConnectedState(Service::ConnectState state);
@@ -469,6 +486,8 @@
   Manager *manager() const { return manager_; }
   const LinkMonitor *link_monitor() const { return link_monitor_.get(); }
   void set_link_monitor(LinkMonitor *link_monitor);
+  // Use for unit test.
+  void set_traffic_monitor(TrafficMonitor *traffic_monitor);
 
  private:
   friend class CellularCapabilityTest;
@@ -593,6 +612,7 @@
       portal_detector_callback_;
   base::Callback<void(const Error &, const IPAddress &)>
       dns_client_callback_;
+  scoped_ptr<TrafficMonitor> traffic_monitor_;
   Technology::Identifier technology_;
   // The number of portal detection attempts from Connected to Online state.
   // This includes all failure/timeout attempts and the final successful
diff --git a/device_unittest.cc b/device_unittest.cc
index 468408b..bc5ad9b 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -92,6 +92,9 @@
         .WillByDefault(Invoke(this, &TestDevice::DeviceIsIPv6Allowed));
     ON_CALL(*this, SetIPFlag(_, _, _))
         .WillByDefault(Invoke(this, &TestDevice::DeviceSetIPFlag));
+    ON_CALL(*this, IsTrafficMonitorEnabled())
+        .WillByDefault(Invoke(this,
+                              &TestDevice::DeviceIsTrafficMonitorEnabled));
   }
 
   ~TestDevice() {}
@@ -107,6 +110,7 @@
   }
 
   MOCK_CONST_METHOD0(IsIPv6Allowed, bool());
+  MOCK_CONST_METHOD0(IsTrafficMonitorEnabled, bool());
 
   MOCK_METHOD3(SetIPFlag, bool(IPAddress::Family family,
                                const std::string &flag,
@@ -116,6 +120,10 @@
     return Device::IsIPv6Allowed();
   }
 
+  virtual bool DeviceIsTrafficMonitorEnabled() const {
+    return Device::IsTrafficMonitorEnabled();
+  }
+
   virtual bool DeviceSetIPFlag(IPAddress::Family family,
                                const std::string &flag,
                                const std::string &value) {
@@ -192,6 +200,22 @@
     return device_->GetLinkMonitorResponseTime(error);
   }
 
+  void SetTrafficMonitor(TrafficMonitor *traffic_monitor) {
+    device_->set_traffic_monitor(traffic_monitor);  // Passes ownership.
+  }
+
+  void StartTrafficMonitor() {
+    device_->StartTrafficMonitor();
+  }
+
+  void StopTrafficMonitor() {
+    device_->StopTrafficMonitor();
+  }
+
+  void NetworkProblemDetected(int reason) {
+    device_->OnEncounterNetworkProblem(reason);
+  }
+
   DeviceMockAdaptor *GetDeviceMockAdaptor() {
     return dynamic_cast<DeviceMockAdaptor *>(device_->adaptor_.get());
   }
@@ -696,6 +720,73 @@
   EXPECT_FALSE(HasLinkMonitor());
 }
 
+TEST_F(DeviceTest, TrafficMonitor) {
+  scoped_refptr<MockConnection> connection(
+      new StrictMock<MockConnection>(&device_info_));
+  MockManager manager(control_interface(),
+                      dispatcher(),
+                      metrics(),
+                      glib());
+  scoped_refptr<MockService> service(
+      new StrictMock<MockService>(control_interface(),
+                                  dispatcher(),
+                                  metrics(),
+                                  &manager));
+  SelectService(service);
+  SetConnection(connection.get());
+  MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
+  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
+  SetManager(&manager);
+
+  EXPECT_CALL(*device_, IsTrafficMonitorEnabled()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*traffic_monitor, Start());
+  StartTrafficMonitor();
+  EXPECT_CALL(*traffic_monitor, Stop());
+  StopTrafficMonitor();
+  Mock::VerifyAndClearExpectations(traffic_monitor);
+
+  EXPECT_CALL(metrics_, NotifyNetworkProblemDetected(_,
+      Metrics::kNetworkProblemDNSFailure)).Times(1);
+  NetworkProblemDetected(TrafficMonitor::kNetworkProblemDNSFailure);
+
+  // Verify traffic monitor not running when it is disabled.
+  traffic_monitor = new StrictMock<MockTrafficMonitor>();
+  SetTrafficMonitor(traffic_monitor);
+  EXPECT_CALL(*device_, IsTrafficMonitorEnabled())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*traffic_monitor, Start()).Times(0);
+  StartTrafficMonitor();
+  EXPECT_CALL(*traffic_monitor, Stop()).Times(0);
+  StopTrafficMonitor();
+
+}
+
+TEST_F(DeviceTest, TrafficMonitorCancelledOnSelectService) {
+  scoped_refptr<MockConnection> connection(
+      new StrictMock<MockConnection>(&device_info_));
+  MockManager manager(control_interface(),
+                      dispatcher(),
+                      metrics(),
+                      glib());
+  scoped_refptr<MockService> service(
+      new StrictMock<MockService>(control_interface(),
+                                  dispatcher(),
+                                  metrics(),
+                                  &manager));
+  SelectService(service);
+  SetConnection(connection.get());
+  MockTrafficMonitor *traffic_monitor = new StrictMock<MockTrafficMonitor>();
+  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
+  EXPECT_CALL(*device_, IsTrafficMonitorEnabled()).WillRepeatedly(Return(true));
+  SetManager(&manager);
+  EXPECT_CALL(*service, state())
+      .WillOnce(Return(Service::kStateIdle));
+  EXPECT_CALL(*service, SetState(_));
+  EXPECT_CALL(*service, SetConnection(_));
+  EXPECT_CALL(*traffic_monitor, Stop());
+  SelectService(NULL);
+}
+
 TEST_F(DeviceTest, ShouldUseArpGateway) {
   EXPECT_FALSE(device_->ShouldUseArpGateway());
 }
diff --git a/metrics.cc b/metrics.cc
index a6c91c9..d42e2b3 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -304,6 +304,10 @@
 const char Metrics::kMetricFallbackDNSTestResult[] =
     "Network.Shill.FallbackDNSTestResult";
 
+// static
+const char Metrics::kMetricNetworkProblemDetectedSuffix[] =
+    "NetworkProblemDetected";
+
 Metrics::Metrics(EventDispatcher *dispatcher)
     : dispatcher_(dispatcher),
       library_(&metrics_library_),
@@ -1134,6 +1138,15 @@
                 kDNSTestResultMax);
 }
 
+void Metrics::NotifyNetworkProblemDetected(Technology::Identifier technology_id,
+                                           int reason) {
+  string histogram = GetFullMetricName(kMetricNetworkProblemDetectedSuffix,
+                                       technology_id);
+  SendEnumToUMA(histogram,
+                reason,
+                kNetworkProblemMax);
+}
+
 bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
   SLOG(Metrics, 5)
       << "Sending enum " << name << " with value " << sample << ".";
diff --git a/metrics.h b/metrics.h
index 58ea4fd..9abdd0e 100644
--- a/metrics.h
+++ b/metrics.h
@@ -291,6 +291,13 @@
     kDNSTestResultMax
   };
 
+  // Network problem detected by traffic monitor.
+  enum NetworkProblem {
+    kNetworkProblemCongestedTCPTxQueue = 0,
+    kNetworkProblemDNSFailure,
+    kNetworkProblemMax
+  };
+
   static const char kMetricDisconnectSuffix[];
   static const int kMetricDisconnectMax;
   static const int kMetricDisconnectMin;
@@ -494,6 +501,9 @@
   // DNS test result.
   static const char kMetricFallbackDNSTestResult[];
 
+  // Network problem detected by traffic monitor
+  static const char kMetricNetworkProblemDetectedSuffix[];
+
   explicit Metrics(EventDispatcher *dispatcher);
   virtual ~Metrics();
 
@@ -683,6 +693,11 @@
   // Notifies this object about the result of the fallback DNS test.
   virtual void NotifyFallbackDNSTestResult(int result);
 
+  // Notifies this object about a network problem detected on the currently
+  // connected network.
+  virtual void NotifyNetworkProblemDetected(
+      Technology::Identifier technology_id, int reason);
+
   // Sends linear histogram data to UMA.
   virtual bool SendEnumToUMA(const std::string &name, int sample, int max);
 
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index a32b2fc..111d8a4 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -799,6 +799,15 @@
   metrics_.NotifyFallbackDNSTestResult(Metrics::kDNSTestResultSuccess);
 }
 
+TEST_F(MetricsTest, NotifyNetworkProblemDetected) {
+  EXPECT_CALL(library_,
+      SendEnumToUMA("Network.Shill.Wifi.NetworkProblemDetected",
+                    Metrics::kNetworkProblemDNSFailure,
+                    Metrics::kNetworkProblemMax));
+  metrics_.NotifyNetworkProblemDetected(Technology::kWifi,
+                                        Metrics::kNetworkProblemDNSFailure);
+}
+
 #ifndef NDEBUG
 
 typedef MetricsTest MetricsDeathTest;
diff --git a/mock_metrics.h b/mock_metrics.h
index e050a59..35087d7 100644
--- a/mock_metrics.h
+++ b/mock_metrics.h
@@ -50,6 +50,8 @@
   MOCK_METHOD2(NotifyUserInitiatedConnectionResult,
                void(const std::string &name, int result));
   MOCK_METHOD1(NotifyFallbackDNSTestResult, void(int result));
+  MOCK_METHOD2(NotifyNetworkProblemDetected,
+               void(Technology::Identifier technology_id, int reason));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockMetrics);
diff --git a/traffic_monitor.cc b/traffic_monitor.cc
index 4bca373..1b623a3 100644
--- a/traffic_monitor.cc
+++ b/traffic_monitor.cc
@@ -190,20 +190,22 @@
 void TrafficMonitor::SampleTraffic() {
   SLOG(Link, 3) << __func__;
 
+  // Schedule the sample callback first, so it is possible for the network
+  // problem callback to stop the traffic monitor.
+  dispatcher_->PostDelayedTask(sample_traffic_callback_.callback(),
+                               kSamplingIntervalMilliseconds);
+
   if (IsCongestedTxQueues() &&
       accummulated_congested_tx_queues_samples_ ==
           kMinimumFailedSamplesToTrigger) {
     LOG(WARNING) << "Congested tx queues detected, out-of-credits?";
-    outgoing_tcp_packets_not_routed_callback_.Run();
+    network_problem_detected_callback_.Run(kNetworkProblemCongestedTxQueue);
   } else if (IsDnsFailing() &&
              accummulated_dns_failures_samples_ ==
                  kMinimumFailedSamplesToTrigger) {
     LOG(WARNING) << "DNS queries failing, out-of-credits?";
-    outgoing_tcp_packets_not_routed_callback_.Run();
+    network_problem_detected_callback_.Run(kNetworkProblemDNSFailure);
   }
-
-  dispatcher_->PostDelayedTask(sample_traffic_callback_.callback(),
-                               kSamplingIntervalMilliseconds);
 }
 
 }  // namespace shill
diff --git a/traffic_monitor.h b/traffic_monitor.h
index 2f19876..4a6ba1a 100644
--- a/traffic_monitor.h
+++ b/traffic_monitor.h
@@ -25,7 +25,14 @@
 // and notifies an observer of various scenarios via callbacks.
 class TrafficMonitor {
  public:
-  typedef base::Closure TcpOutTrafficNotRoutedCallback;
+  // Network problem detected by traffic monitor.
+  enum NetworkProblem {
+    kNetworkProblemCongestedTxQueue=0,
+    kNetworkProblemDNSFailure,
+    kNetworkProblemMax
+  };
+
+  typedef base::Callback<void(int)> NetworkProblemDetectedCallback;
 
   TrafficMonitor(const DeviceRefPtr &device, EventDispatcher *dispatcher);
   virtual ~TrafficMonitor();
@@ -36,11 +43,12 @@
   // Stops traffic monitoring on the selected device.
   virtual void Stop();
 
-  // Sets the callback to invoke, if the traffic monitor detects that too many
-  // packets are failing to get transmitted over a TCP connection.
-  void set_tcp_out_traffic_not_routed_callback(
-      const TcpOutTrafficNotRoutedCallback &callback) {
-    outgoing_tcp_packets_not_routed_callback_ = callback;
+  // Sets the callback to invoke, if the traffic monitor detects a network
+  // problem, either too many packets are failing to get transmitted over a
+  // TCP connection or DNS is failing.
+  void set_network_problem_detected_callback(
+      const NetworkProblemDetectedCallback &callback) {
+    network_problem_detected_callback_ = callback;
   }
 
  private:
@@ -121,9 +129,11 @@
   // of the network interface.
   base::CancelableClosure sample_traffic_callback_;
 
-  // Callback to invoke when we detect that the send queue has been increasing
-  // on an ESTABLISHED TCP connection through the network interface.
-  TcpOutTrafficNotRoutedCallback outgoing_tcp_packets_not_routed_callback_;
+  // Callback to invoke when we detect a network problem. Possible network
+  // problems that can be detected are congested TCP TX queue and DNS failure.
+  // Refer to enum NetworkProblem for all possible network problems that can be
+  // detected by Traffic Monitor.
+  NetworkProblemDetectedCallback network_problem_detected_callback_;
 
   // Reads and parses socket information from the system.
   scoped_ptr<SocketInfoReader> socket_info_reader_;
diff --git a/traffic_monitor_unittest.cc b/traffic_monitor_unittest.cc
index e4ee9a5..225f289 100644
--- a/traffic_monitor_unittest.cc
+++ b/traffic_monitor_unittest.cc
@@ -63,7 +63,7 @@
     remote_addr_.SetAddressFromString(kRemoteIpAddr);
   }
 
-  MOCK_METHOD0(OnNoOutgoingPackets, void());
+  MOCK_METHOD1(OnNoOutgoingPackets, void(int));
 
  protected:
   virtual void SetUp() {
@@ -314,20 +314,21 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
   // Mimic same queue length by using same mock socket info.
-  EXPECT_CALL(*this, OnNoOutgoingPackets());
+  EXPECT_CALL(*this, OnNoOutgoingPackets(
+      TrafficMonitor::kNetworkProblemCongestedTxQueue));
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
   // Perform another sampling pass and make sure the callback is only
   // triggered once.
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
 }
 
@@ -343,9 +344,9 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
@@ -360,7 +361,8 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  EXPECT_CALL(*this, OnNoOutgoingPackets());
+  EXPECT_CALL(*this, OnNoOutgoingPackets(
+      TrafficMonitor::kNetworkProblemCongestedTxQueue));
   monitor_.SampleTraffic();
 }
 
@@ -376,9 +378,9 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
@@ -393,7 +395,7 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
@@ -408,7 +410,8 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  EXPECT_CALL(*this, OnNoOutgoingPackets());
+  EXPECT_CALL(*this, OnNoOutgoingPackets(
+      TrafficMonitor::kNetworkProblemCongestedTxQueue));
   monitor_.SampleTraffic();
 }
 
@@ -424,9 +427,9 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
 
   socket_infos.clear();
@@ -456,9 +459,9 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
 
   socket_infos.clear();
@@ -479,9 +482,9 @@
                  0,
                  SocketInfo::kTimerStateRetransmitTimerPending));
   SetupMockSocketInfos(socket_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
 
   socket_infos.clear();
@@ -509,11 +512,11 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
   // Make sure the no routing event is not fired before the threshold is
   // exceeded.
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -521,12 +524,13 @@
   Mock::VerifyAndClearExpectations(this);
 
   // This call should cause the threshold to exceed.
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(1);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(
+      TrafficMonitor::kNetworkProblemDNSFailure)).Times(1);
   monitor_.SampleTraffic();
   Mock::VerifyAndClearExpectations(this);
 
   // Make sure the event is only fired once.
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
 }
 
@@ -540,9 +544,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -559,9 +563,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -578,9 +582,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 1; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -596,7 +600,7 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   monitor_.SampleTraffic();
   EXPECT_EQ(0, monitor_.accummulated_dns_failures_samples_);
 }
@@ -611,9 +615,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -630,9 +634,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    remote_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -650,9 +654,9 @@
                    remote_addr_, TrafficMonitor::kDnsPort,
                    remote_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
@@ -670,9 +674,9 @@
                    remote_addr_, kNonDnsPort,
                    local_addr_, TrafficMonitorTest::kLocalPort1));
   SetupMockConnectionInfos(connection_infos);
-  monitor_.set_tcp_out_traffic_not_routed_callback(
+  monitor_.set_network_problem_detected_callback(
       Bind(&TrafficMonitorTest::OnNoOutgoingPackets, Unretained(this)));
-  EXPECT_CALL(*this, OnNoOutgoingPackets()).Times(0);
+  EXPECT_CALL(*this, OnNoOutgoingPackets(_)).Times(0);
   for (int count = 0; count < TrafficMonitor::kMinimumFailedSamplesToTrigger;
        ++count) {
     monitor_.SampleTraffic();
diff --git a/wifi.cc b/wifi.cc
index 51b05c7..8e5dcc1 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -2581,6 +2581,11 @@
   return "";
 }
 
+// Traffic monitor is enabled for wifi.
+bool WiFi::IsTrafficMonitorEnabled() const {
+  return true;
+}
+
 bool WiFi::ResolvePeerMacAddress(const string &input, string *output,
                                  Error *error) {
   if (!WiFiEndpoint::MakeHardwareAddressFromString(input).empty()) {
diff --git a/wifi.h b/wifi.h
index 1747676..440e61d 100644
--- a/wifi.h
+++ b/wifi.h
@@ -216,6 +216,10 @@
   virtual std::string PerformTDLSOperation(const std::string &operation,
                                            const std::string &peer,
                                            Error *error) override;
+
+  // Overridden from Device superclass.
+  virtual bool IsTrafficMonitorEnabled() const override;
+
  private:
   enum ScanMethod {
     kScanMethodNone,