shill: Exposing wifi disconnect reason to UMA.

This change provides UMA with a reason for each WiFi deauthentication
that occurs.  These values are extracted from the deauthentication
message that we get from the mac80211 drivers in the kernel (because
we've asked that driver to send us events).

BUG=chromium-os:33637,26854
TEST=Manual (disconnected in various ways and verified with UMA).  Ran
shill unit tests.

Change-Id: I630ffbba02b40484fc3c6b20e444e71093cfce65
Reviewed-on: https://gerrit.chromium.org/gerrit/31475
Commit-Ready: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/metrics.cc b/metrics.cc
index dc83a50..9bc7e9e 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -9,6 +9,7 @@
 #include <chromeos/dbus/service_constants.h>
 #include <metrics/bootstat.h>
 
+#include "shill/ieee80211.h"
 #include "shill/link_monitor.h"
 #include "shill/logging.h"
 #include "shill/wifi_service.h"
@@ -124,6 +125,17 @@
 const int Metrics::kMetricLinkMonitorErrorCountNumBuckets =
     LinkMonitor::kFailureThreshold + 1;
 
+// static
+const char Metrics::kMetricLinkClientDisconnectReason[] =
+    "Network.Shill.WiFi.ClientDisconnectReason";
+const char Metrics::kMetricLinkApDisconnectReason[] =
+    "Network.Shill.WiFi.ApDisconnectReason";
+const char Metrics::kMetricLinkClientDisconnectType[] =
+    "Network.Shill.WiFi.ClientDisconnectType";
+const char Metrics::kMetricLinkApDisconnectType[] =
+    "Network.Shill.WiFi.ApDisconnectType";
+
+
 Metrics::Metrics()
     : library_(&metrics_library_),
       last_default_technology_(Technology::kUnknown),
@@ -451,6 +463,39 @@
             kMetricLinkMonitorResponseTimeSampleNumBuckets);
 }
 
+void Metrics::Notify80211Disconnect(WiFiDisconnectByWhom by_whom,
+                                    IEEE_80211::WiFiReasonCode reason) {
+  string metric_disconnect_reason;
+  string metric_disconnect_type;
+  WiFiStatusType type;
+
+  if (by_whom == kDisconnectedByAp) {
+    metric_disconnect_reason = kMetricLinkApDisconnectReason;
+    metric_disconnect_type = kMetricLinkApDisconnectType;
+    type = kStatusCodeTypeByAp;
+  } else {
+    metric_disconnect_reason = kMetricLinkClientDisconnectReason;
+    metric_disconnect_type = kMetricLinkClientDisconnectType;
+    switch(reason) {
+      case IEEE_80211::kReasonCodeSenderHasLeft:
+      case IEEE_80211::kReasonCodeDisassociatedHasLeft:
+        type = kStatusCodeTypeByUser;
+        break;
+
+      case IEEE_80211::kReasonCodeInactivity:
+        type = kStatusCodeTypeConsideredDead;
+        break;
+
+      default:
+        type = kStatusCodeTypeByClient;
+        break;
+    }
+  }
+  SendEnumToUMA(metric_disconnect_reason, reason,
+                IEEE_80211::kStatusCodeMax);
+  SendEnumToUMA(metric_disconnect_type, type, kStatusCodeTypeMax);
+}
+
 bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
   return library_->SendEnumToUMA(name, sample, max);
 }