shill: Adds UMA metrics for scan termination reason.

BUG=chromium:245814
TEST=unittest and manual.  For manual tests, do the following:

  * reboot the machine (to force a scan)
  * navigate to chrome//histograms
  * verify that there's an entry for Network.Shill.WiFi.ScanResult

Change-Id: I36613ad1eb6682dfca880d94b11ec4eeacb442bc
Reviewed-on: https://gerrit.chromium.org/gerrit/57275
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/metrics.cc b/metrics.cc
index 4fb083c..71b4f95 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -139,6 +139,8 @@
 const int Metrics::kMetricFrequenciesConnectedMin = 1;
 const int Metrics::kMetricFrequenciesConnectedNumBuckets = 50;
 
+const char Metrics::kMetricScanResult[] =
+    "Network.Shill.WiFi.ScanResult";
 const char Metrics::kMetricWiFiScanTimeInEbusyMilliseconds[] =
     "Network.Shill.WiFi.ScanTimeInEbusy";
 
diff --git a/metrics.h b/metrics.h
index d95101a..979cdb5 100644
--- a/metrics.h
+++ b/metrics.h
@@ -166,6 +166,18 @@
     kDisconnectedNotByAp
   };
 
+  enum WiFiScanResult {
+    kScanResultProgressiveConnected,
+    kScanResultProgressiveErrorAndFullFoundNothing,
+    kScanResultProgressiveErrorButFullConnected,
+    kScanResultProgressiveAndFullFoundNothing,
+    kScanResultProgressiveAndFullConnected,
+    kScanResultFullScanFoundNothing,
+    kScanResultFullScanConnected,
+    kScanResultInternalError,
+    kScanResultMax
+  };
+
   enum ServiceFixupProfileType {
     kMetricServiceFixupDefaultProfile,
     kMetricServiceFixupUserProfile,
@@ -316,6 +328,7 @@
   static const int kMetricFrequenciesConnectedMin;
   static const int kMetricFrequenciesConnectedNumBuckets;
 
+  static const char kMetricScanResult[];
   static const char kMetricWiFiScanTimeInEbusyMilliseconds[];
 
   static const char kMetricPowerManagerKey[];
diff --git a/wifi.cc b/wifi.cc
index 859712b..d5ed6f2 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -1855,6 +1855,12 @@
 void WiFi::SetScanState(ScanState new_state, ScanMethod new_method) {
   if (new_state == kScanIdle)
     new_method = kScanMethodNone;
+  if (new_state == kScanConnected) {
+    // The scan method shouldn't be changed by the connection process, so
+    // we'll put a CHECK, here, to verify.  NOTE: this assumption is also
+    // enforced by the parameters to the call to |ReportScanResultToUma|.
+    CHECK(new_method == scan_method_);
+  }
 
   int log_level = 6;
   bool state_changed = true;
@@ -1890,6 +1896,7 @@
 
   // Actually change the state.
   ScanState old_state = scan_state_;
+  ScanMethod old_method = scan_method_;
   scan_state_ = new_state;
   scan_method_ = new_method;
   if (new_state == kScanScanning || old_state == kScanScanning) {
@@ -1897,6 +1904,7 @@
     adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
                                GetScanPending(&error));
   }
+  ReportScanResultToUma(new_state, old_method);
   if (is_terminal_state) {
     // Now that we've logged a terminal state, let's call ourselves to
     // transistion to the idle state.
@@ -1976,4 +1984,48 @@
   return "";  // To shut up the compiler (that doesn't understand NOTREACHED).
 }
 
+void WiFi::ReportScanResultToUma(ScanState state, ScanMethod method) {
+  Metrics::WiFiScanResult result = Metrics::kScanResultMax;
+  if (state == kScanConnected) {
+    switch (method) {
+      case kScanMethodFull:
+        result = Metrics::kScanResultFullScanConnected;
+        break;
+      case kScanMethodProgressive:
+        result = Metrics::kScanResultProgressiveConnected;
+        break;
+      case kScanMethodProgressiveErrorToFull:
+        result = Metrics::kScanResultProgressiveErrorButFullConnected;
+        break;
+      case kScanMethodProgressiveFinishedToFull:
+        result = Metrics::kScanResultProgressiveAndFullConnected;
+        break;
+      default:
+        // OK: Connect resulting from something other than scan.
+        break;
+    }
+  } else if (state == kScanFoundNothing) {
+    switch (method) {
+      case kScanMethodFull:
+        result = Metrics::kScanResultFullScanFoundNothing;
+        break;
+      case kScanMethodProgressiveErrorToFull:
+        result = Metrics::kScanResultProgressiveErrorAndFullFoundNothing;
+        break;
+      case kScanMethodProgressiveFinishedToFull:
+        result = Metrics::kScanResultProgressiveAndFullFoundNothing;
+        break;
+      default:
+        // OK: Connect failed, not scan related.
+        break;
+    }
+  }
+
+  if (result != Metrics::kScanResultMax) {
+    metrics()->SendEnumToUMA(Metrics::kMetricScanResult,
+                             result,
+                             Metrics::kScanResultMax);
+  }
+}
+
 }  // namespace shill
diff --git a/wifi.h b/wifi.h
index 3d1e446..f5bdcbb 100644
--- a/wifi.h
+++ b/wifi.h
@@ -92,6 +92,7 @@
 #include "shill/dbus_manager.h"
 #include "shill/device.h"
 #include "shill/event_dispatcher.h"
+#include "shill/metrics.h"
 #include "shill/power_manager.h"
 #include "shill/refptr_types.h"
 #include "shill/service.h"
@@ -227,6 +228,7 @@
   FRIEND_TEST(WiFiMainTest, ProgressiveScanNotFound);  // ScanMethod, ScanState
   FRIEND_TEST(WiFiMainTest, ScanResults);             // EndpointMap
   FRIEND_TEST(WiFiMainTest, ScanResultsWithUpdates);  // EndpointMap
+  FRIEND_TEST(WiFiMainTest, ScanStateUma);  // ScanState, ScanMethod
   FRIEND_TEST(WiFiMainTest, Stop);  // weak_ptr_factory_
   FRIEND_TEST(WiFiMainTest, TimeoutPendingServiceWithEndpoints);
   FRIEND_TEST(WiFiMainTest, VerifyPaths);
@@ -410,6 +412,7 @@
   void OnNewWiphy(const Nl80211Message &nl80211_message);
 
   void SetScanState(ScanState new_state, ScanMethod new_method);
+  void ReportScanResultToUma(ScanState state, ScanMethod method);
   static std::string ScanStateString(ScanState state, ScanMethod type);
 
   // Pointer to the provider object that maintains WiFiService objects.
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index a818217..4c684e2 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -358,6 +358,10 @@
     wifi_->scan_session_->OnTriggerScanResponse(message);
   }
 
+  void SetScanState(WiFi::ScanState new_state, WiFi::ScanMethod new_method) {
+    wifi_->SetScanState(new_state, new_method);
+  }
+
   void VerifyScanState(WiFi::ScanState state, WiFi::ScanMethod method) {
     EXPECT_EQ(state, wifi_->scan_state_);
     EXPECT_EQ(method, wifi_->scan_method_);
@@ -2754,4 +2758,13 @@
   ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
 }
 
+TEST_F(WiFiMainTest, ScanStateUma) {
+  EXPECT_CALL(*metrics(), SendEnumToUMA(Metrics::kMetricScanResult, _, _)).
+      Times(0);
+  SetScanState(WiFi::kScanScanning, WiFi::kScanMethodProgressive);
+
+  EXPECT_CALL(*metrics(), SendEnumToUMA(Metrics::kMetricScanResult, _, _));
+  SetScanState(WiFi::kScanConnected, WiFi::kScanMethodProgressive);
+}
+
 }  // namespace shill