shill: Putting UMA metrics in a separate nl80211 callback.

BUG=chromium-os:35466
TEST=Manual and unit tests.

Change-Id: I908f887a7ff3ce4d0bf33f1096cd61155c20e14a
Reviewed-on: https://gerrit.chromium.org/gerrit/35963
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Wade Guthrie <wdg@google.com>
Tested-by: Wade Guthrie <wdg@google.com>
diff --git a/callback80211_metrics.cc b/callback80211_metrics.cc
new file mode 100644
index 0000000..20cd6f6
--- /dev/null
+++ b/callback80211_metrics.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/callback80211_metrics.h"
+
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+
+#include "shill/config80211.h"
+#include "shill/ieee80211.h"
+#include "shill/link_monitor.h"
+#include "shill/logging.h"
+#include "shill/metrics.h"
+#include "shill/scope_logger.h"
+#include "shill/user_bound_nlmessage.h"
+
+using base::Bind;
+using std::string;
+
+namespace shill {
+
+Callback80211Metrics::Callback80211Metrics(Config80211 *config80211,
+                                           Metrics *metrics)
+    : Callback80211Object(config80211), metrics_(metrics),
+      weak_ptr_factory_(this) {
+}
+
+void Callback80211Metrics::Config80211MessageCallback(
+    const UserBoundNlMessage &message) {
+  if (metrics_ && message.GetMessageType() == DeauthenticateMessage::kCommand) {
+    Metrics::WiFiDisconnectByWhom by_whom =
+        message.AttributeExists(NL80211_ATTR_DISCONNECTED_BY_AP) ?
+                    Metrics::kDisconnectedByAp : Metrics::kDisconnectedNotByAp;
+    uint16_t reason = static_cast<uint16_t>(
+        IEEE_80211::kReasonCodeInvalid);
+    void *rawdata = NULL;
+    int frame_byte_count = 0;
+    if (message.GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata,
+                                    &frame_byte_count)) {
+      const uint8_t *frame_data = reinterpret_cast<const uint8_t *>(rawdata);
+      Nl80211Frame frame(frame_data, frame_byte_count);
+      reason = frame.reason();
+    }
+    IEEE_80211::WiFiReasonCode reason_enum =
+        static_cast<IEEE_80211::WiFiReasonCode>(reason);
+    metrics_->Notify80211Disconnect(by_whom, reason_enum);
+  }
+}
+
+bool Callback80211Metrics::InstallAsDefaultCallback() {
+  if (config80211_) {
+    callback_ = Bind(&Callback80211Metrics::Config80211MessageCallback,
+                     weak_ptr_factory_.GetWeakPtr());
+    config80211_->SetDefaultCallback(callback_);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace shill.