OSDN Git Service

Monitor mlme event for wificond
authorNingyuan Wang <nywang@google.com>
Wed, 5 Oct 2016 21:25:22 +0000 (14:25 -0700)
committerNingyuan Wang <nywang@google.com>
Tue, 25 Oct 2016 20:34:19 +0000 (13:34 -0700)
1. This allows NetlinkManager to monitor MLME events.
2. This adds internal API for tracking MLME events.
3. This creates classes representing different MLME events.
4. ClientInterfaceImpl will update frequency and bssid upon
   ASSOCIATE/CONNECT/ROAM events.

Bug: 31961586
Test: compile, manual tests

Change-Id: I1bd62dfa85596fd262709038fb73fc6e0ff9c5f3

Android.mk
client_interface_impl.cpp
client_interface_impl.h
net/mlme_event.cpp [new file with mode: 0644]
net/mlme_event.h [new file with mode: 0644]
net/mlme_event_handler.h [new file with mode: 0644]
net/netlink_manager.cpp
net/netlink_manager.h
net/netlink_utils.cpp
net/netlink_utils.h

index 53297a8..b65ae05 100644 (file)
@@ -77,6 +77,7 @@ LOCAL_MODULE := libwificond_nl
 LOCAL_CPPFLAGS := $(wificond_cpp_flags)
 LOCAL_C_INCLUDES := $(wificond_includes)
 LOCAL_SRC_FILES := \
+    net/mlme_event.cpp \
     net/netlink_manager.cpp \
     net/netlink_utils.cpp \
     net/nl80211_attribute.cpp \
index 36f93ac..e5a0b4c 100644 (file)
@@ -23,6 +23,7 @@
 #include <wifi_system/wifi.h>
 
 #include "wificond/client_interface_binder.h"
+#include "wificond/net/mlme_event.h"
 #include "wificond/net/netlink_utils.h"
 #include "wificond/scanning/scan_result.h"
 #include "wificond/scanning/scan_utils.h"
@@ -40,6 +41,34 @@ using std::vector;
 namespace android {
 namespace wificond {
 
+MlmeEventHandlerImpl::MlmeEventHandlerImpl(ClientInterfaceImpl* client_interface)
+    : client_interface_(client_interface) {
+}
+
+MlmeEventHandlerImpl::~MlmeEventHandlerImpl() {
+}
+
+void MlmeEventHandlerImpl::OnConnect(unique_ptr<MlmeConnectEvent> event) {
+  if (event->GetStatusCode() == 0) {
+    client_interface_->RefreshAssociateFreq();
+    client_interface_->bssid_ = event->GetBSSID();
+  }
+}
+
+void MlmeEventHandlerImpl::OnRoam(unique_ptr<MlmeRoamEvent> event) {
+  if (event->GetStatusCode() == 0) {
+    client_interface_->RefreshAssociateFreq();
+    client_interface_->bssid_ = event->GetBSSID();
+  }
+}
+
+void MlmeEventHandlerImpl::OnAssociate(unique_ptr<MlmeAssociateEvent> event) {
+  if (event->GetStatusCode() == 0) {
+    client_interface_->RefreshAssociateFreq();
+    client_interface_->bssid_ = event->GetBSSID();
+  }
+}
+
 ClientInterfaceImpl::ClientInterfaceImpl(
     const std::string& interface_name,
     uint32_t interface_index,
@@ -55,18 +84,23 @@ ClientInterfaceImpl::ClientInterfaceImpl(
       supplicant_manager_(supplicant_manager),
       netlink_utils_(netlink_utils),
       scan_utils_(scan_utils),
+      mlme_event_handler_(new MlmeEventHandlerImpl(this)),
       binder_(new ClientInterfaceBinder(this)) {
   scan_utils_->SubscribeScanResultNotification(
       interface_index_,
       std::bind(&ClientInterfaceImpl::OnScanResultsReady,
                 this,
                 _1, _2, _3, _4));
+  netlink_utils_->SubscribeMlmeEvent(
+      interface_index_,
+      mlme_event_handler_.get());
 }
 
 ClientInterfaceImpl::~ClientInterfaceImpl() {
   binder_->NotifyImplDead();
   DisableSupplicant();
   scan_utils_->UnsubscribeScanResultNotification(interface_index_);
+  netlink_utils_->UnsubscribeMlmeEvent(interface_index_);
   if_tool_->SetUpState(interface_name_.c_str(), false);
 }
 
@@ -146,5 +180,20 @@ bool ClientInterfaceImpl::requestANQP(
   return true;
 }
 
+bool ClientInterfaceImpl::RefreshAssociateFreq() {
+  // wpa_supplicant fetches associate frequency using the latest scan result.
+  // We should follow the same method here before we find a better solution.
+  std::vector<ScanResult> scan_results;
+  if (!scan_utils_->GetScanResult(interface_index_, &scan_results)) {
+    return false;
+  }
+  for (auto& scan_result : scan_results) {
+    if (scan_result.associated) {
+      associate_freq_ = scan_result.frequency;
+    }
+  }
+  return false;
+}
+
 }  // namespace wificond
 }  // namespace android
index 195e61b..72f581f 100644 (file)
 #include <wifi_system/supplicant_manager.h>
 
 #include "android/net/wifi/IClientInterface.h"
+#include "wificond/net/mlme_event_handler.h"
 
 namespace android {
 namespace wificond {
 
 class ClientInterfaceBinder;
+class ClientInterfaceImpl;
 class NetlinkUtils;
 class ScanUtils;
 
+class MlmeEventHandlerImpl : public MlmeEventHandler {
+ public:
+  MlmeEventHandlerImpl(ClientInterfaceImpl* client_interface);
+  ~MlmeEventHandlerImpl() override;
+  void OnConnect(std::unique_ptr<MlmeConnectEvent> event) override;
+  void OnRoam(std::unique_ptr<MlmeRoamEvent> event) override;
+  void OnAssociate(std::unique_ptr<MlmeAssociateEvent> event) override;
+
+ private:
+  ClientInterfaceImpl* client_interface_;
+};
+
 // Holds the guts of how we control network interfaces capable of connecting to
 // access points via wpa_supplicant.
 //
@@ -70,6 +84,7 @@ class ClientInterfaceImpl {
                           std::vector<std::vector<uint8_t>>& ssids,
                           std::vector<uint32_t>& frequencies);
   void OnSchedScanResultsReady(uint32_t interface_index);
+  bool RefreshAssociateFreq();
 
   const std::string interface_name_;
   const uint32_t interface_index_;
@@ -78,9 +93,14 @@ class ClientInterfaceImpl {
   android::wifi_system::SupplicantManager* const supplicant_manager_;
   NetlinkUtils* const netlink_utils_;
   ScanUtils* const scan_utils_;
+  const std::unique_ptr<MlmeEventHandlerImpl> mlme_event_handler_;
   const android::sp<ClientInterfaceBinder> binder_;
 
+  std::vector<uint8_t> bssid_;
+  uint32_t associate_freq_;
+
   DISALLOW_COPY_AND_ASSIGN(ClientInterfaceImpl);
+  friend class MlmeEventHandlerImpl;
 };
 
 }  // namespace wificond
diff --git a/net/mlme_event.cpp b/net/mlme_event.cpp
new file mode 100644 (file)
index 0000000..478eb1c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wificond/net/mlme_event.h"
+
+#include <vector>
+
+#include <linux/nl80211.h>
+
+#include <android-base/logging.h>
+
+#include "wificond/net/nl80211_packet.h"
+
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace wificond {
+
+namespace {
+
+bool GetCommonFields(const NL80211Packet* packet,
+                     uint32_t* if_index,
+                     vector<uint8_t>* bssid) {
+  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, if_index)) {
+     LOG(ERROR) << "Failed to get NL80211_ATTR_IFINDEX";
+     return false;
+  }
+  if (!packet->GetAttributeValue(NL80211_ATTR_MAC, bssid)) {
+    LOG(ERROR) << "Failed to get NL80211_ATTR_MAC";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+unique_ptr<MlmeAssociateEvent> MlmeAssociateEvent::InitFromPacket(
+    const NL80211Packet* packet) {
+  if (packet->GetCommand() != NL80211_CMD_ASSOCIATE) {
+    return nullptr;
+  }
+  unique_ptr<MlmeAssociateEvent> associate_event(new MlmeAssociateEvent());
+
+  if (!GetCommonFields(packet,
+                       &(associate_event->interface_index_),
+                       &(associate_event->bssid_))){
+    return nullptr;
+  }
+  // According to wpa_supplicant, status code of an ASSOCIATE event should be
+  // parsed from NL80211_ATTR_FRAME attribute.
+  // TODO(nywang): Parse NL80211_ATTR_FRAME 80211 management frame and get
+  // status code.
+  associate_event->status_code_ = 0;
+  return associate_event;
+}
+
+unique_ptr<MlmeConnectEvent> MlmeConnectEvent::InitFromPacket(
+    const NL80211Packet* packet) {
+  if (packet->GetCommand() != NL80211_CMD_CONNECT) {
+    return nullptr;
+  }
+  unique_ptr<MlmeConnectEvent> connect_event(new MlmeConnectEvent());
+  if (!GetCommonFields(packet,
+                       &(connect_event->interface_index_),
+                       &(connect_event->bssid_))){
+    return nullptr;
+  }
+
+  if (!packet->GetAttributeValue(NL80211_ATTR_STATUS_CODE,
+                                 &(connect_event->status_code_))) {
+    LOG(WARNING) << "Failed to get NL80211_ATTR_STATUS_CODE";
+    connect_event->status_code_ = 0;
+  }
+  return connect_event;
+}
+
+unique_ptr<MlmeRoamEvent> MlmeRoamEvent::InitFromPacket(
+    const NL80211Packet* packet) {
+  if (packet->GetCommand() != NL80211_CMD_CONNECT) {
+    return nullptr;
+  }
+  unique_ptr<MlmeRoamEvent> roam_event(new MlmeRoamEvent());
+  if (!GetCommonFields(packet,
+                       &(roam_event->interface_index_),
+                       &(roam_event->bssid_))){
+    return nullptr;
+  }
+
+  if (!packet->GetAttributeValue(NL80211_ATTR_STATUS_CODE,
+                                 &(roam_event->status_code_))) {
+    LOG(WARNING) << "Failed to get NL80211_ATTR_STATUS_CODE";
+    roam_event->status_code_ = 0;
+  }
+
+  return roam_event;
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/net/mlme_event.h b/net/mlme_event.h
new file mode 100644 (file)
index 0000000..90f8044
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFICOND_NET_MLME_EVENT_H_
+#define WIFICOND_NET_MLME_EVENT_H_
+
+#include <memory>
+#include <vector>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wificond {
+
+class NL80211Packet;
+
+class MlmeConnectEvent {
+ public:
+  static std::unique_ptr<MlmeConnectEvent> InitFromPacket(
+      const NL80211Packet* packet);
+  // Returns the BSSID of the associated AP.
+  const std::vector<uint8_t>& GetBSSID() const { return bssid_; }
+  // Get the status code of this connect event.
+  // 0 = success, non-zero = failure.
+  // Status codes definition: IEEE 802.11-2012, 8.4.1.9, Table 8-37
+  uint16_t GetStatusCode() const { return status_code_; }
+  uint32_t GetInterfaceIndex() const { return interface_index_; }
+
+ private:
+  MlmeConnectEvent() = default;
+
+  uint32_t interface_index_;
+  std::vector<uint8_t> bssid_;
+  uint16_t status_code_;
+
+  DISALLOW_COPY_AND_ASSIGN(MlmeConnectEvent);
+};
+
+class MlmeAssociateEvent {
+ public:
+  static std::unique_ptr<MlmeAssociateEvent> InitFromPacket(
+      const NL80211Packet* packet);
+  // Returns the BSSID of the associated AP.
+  const std::vector<uint8_t>& GetBSSID() const { return bssid_; }
+  // Get the status code of this associate event.
+  // 0 = success, non-zero = failure.
+  // Status codes definition: IEEE 802.11-2012, 8.4.1.9, Table 8-37
+  uint16_t GetStatusCode() const { return status_code_; }
+  uint32_t GetInterfaceIndex() const { return interface_index_; }
+
+ private:
+  MlmeAssociateEvent() = default;
+
+  uint32_t interface_index_;
+  std::vector<uint8_t> bssid_;
+  uint16_t status_code_;
+
+  DISALLOW_COPY_AND_ASSIGN(MlmeAssociateEvent);
+};
+
+class MlmeRoamEvent {
+ public:
+  static std::unique_ptr<MlmeRoamEvent> InitFromPacket(
+      const NL80211Packet* packet);
+  // Returns the BSSID of the associated AP.
+  const std::vector<uint8_t>& GetBSSID() const { return bssid_; }
+  // Get the status code of this roam event.
+  // 0 = success, non-zero = failure.
+  // Status codes definition: IEEE 802.11-2012, 8.4.1.9, Table 8-37
+  uint16_t GetStatusCode() const { return status_code_; }
+  uint32_t GetInterfaceIndex() const { return interface_index_; }
+
+ private:
+  MlmeRoamEvent() = default;
+
+  uint32_t interface_index_;
+  std::vector<uint8_t> bssid_;
+  uint16_t status_code_;
+
+  DISALLOW_COPY_AND_ASSIGN(MlmeRoamEvent);
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_NET_MLME_EVENT_H_
diff --git a/net/mlme_event_handler.h b/net/mlme_event_handler.h
new file mode 100644 (file)
index 0000000..fba73b7
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFICOND_NET_MLME_EVENT_HANDLER_H_
+#define WIFICOND_NET_MLME_EVENT_HANDLER_H_
+
+#include <functional>
+#include <memory>
+
+#include <wificond/net/mlme_event.h>
+
+namespace android {
+namespace wificond {
+
+// Abstract class for handling mlme events.
+class MlmeEventHandler {
+ public:
+  virtual ~MlmeEventHandler() {}
+
+  virtual void OnConnect(std::unique_ptr<MlmeConnectEvent> event) = 0;
+  virtual void OnRoam(const std::unique_ptr<MlmeRoamEvent> event) = 0;
+  virtual void OnAssociate(std::unique_ptr<MlmeAssociateEvent> event) = 0;
+
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_NET_MLME_EVENT_HANDLER_H_
index ad50890..16d738f 100644 (file)
@@ -27,6 +27,8 @@
 #include <android-base/logging.h>
 #include <utils/Timers.h>
 
+#include "net/mlme_event.h"
+#include "net/mlme_event_handler.h"
 #include "net/nl80211_attribute.h"
 #include "net/nl80211_packet.h"
 
@@ -219,6 +221,9 @@ bool NetlinkManager::Start() {
   //  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_SCAN)) {
   //    return false;
   //  }
+  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_MLME)) {
+    return false;
+  }
 
   started_ = true;
   return true;
@@ -464,8 +469,64 @@ void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
       // available.
       command == NL80211_CMD_SCAN_ABORTED) {
     OnScanResultsReady(std::move(packet));
-  } else if (command == NL80211_CMD_SCHED_SCAN_RESULTS) {
+    return;
+  }
+
+  if (command == NL80211_CMD_SCHED_SCAN_RESULTS) {
     OnSchedScanResultsReady(std::move(packet));
+    return;
+  }
+
+
+  // Driver which supports SME uses both NL80211_CMD_AUTHENTICATE and
+  // NL80211_CMD_ASSOCIATE, otherwise it uses NL80211_CMD_CONNECT
+  // to notify a combination of authentication and association processses.
+  // Currently we monitor CONNECT/ASSOCIATE/ROAM event for up-to-date
+  // frequency and bssid.
+  // TODO(nywang): Handle other MLME events, which help us track the
+  // connection state better.
+  if (command == NL80211_CMD_CONNECT ||
+      command == NL80211_CMD_ASSOCIATE ||
+      command == NL80211_CMD_ROAM) {
+      OnMlmeEvent(std::move(packet));
+     return;
+  }
+}
+
+void NetlinkManager::OnMlmeEvent(unique_ptr<const NL80211Packet> packet) {
+  uint32_t if_index;
+
+  if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
+    LOG(ERROR) << "Failed to get interface index from a MLME event message";
+    return;
+  }
+  auto handler = on_mlme_event_handler_.find(if_index);
+  if (handler == on_mlme_event_handler_.end()) {
+    LOG(DEBUG) << "No handler for mlme event from interface"
+               << " with index: " << if_index;
+    return;
+  }
+  uint32_t command = packet->GetCommand();
+  if (command == NL80211_CMD_CONNECT) {
+    auto event = MlmeConnectEvent::InitFromPacket(packet.get());
+    if (event != nullptr) {
+       handler->second->OnConnect(std::move(event));
+    }
+    return;
+  }
+  if (command == NL80211_CMD_ASSOCIATE) {
+    auto event = MlmeAssociateEvent::InitFromPacket(packet.get());
+    if (event != nullptr) {
+       handler->second->OnAssociate(std::move(event));
+    }
+    return;
+  }
+  if (command == NL80211_CMD_ROAM) {
+    auto event = MlmeRoamEvent::InitFromPacket(packet.get());
+    if (event != nullptr) {
+       handler->second->OnRoam(std::move(event));
+    }
+    return;
   }
 }
 
@@ -541,6 +602,15 @@ void NetlinkManager::UnsubscribeScanResultNotification(
   on_scan_result_ready_handler_.erase(interface_index);
 }
 
+void NetlinkManager::SubscribeMlmeEvent(uint32_t interface_index,
+                                        MlmeEventHandler* handler) {
+  on_mlme_event_handler_[interface_index] = handler;
+}
+
+void NetlinkManager::UnsubscribeMlmeEvent(uint32_t interface_index) {
+  on_mlme_event_handler_.erase(interface_index);
+}
+
 void NetlinkManager::SubscribeSchedScanResultNotification(
       uint32_t interface_index,
       OnSchedScanResultsReadyHandler handler) {
index c1d35d6..c3acd27 100644 (file)
@@ -29,6 +29,7 @@
 namespace android {
 namespace wificond {
 
+class MlmeEventHandler;
 class NL80211Packet;
 
 // Encapsulates all the different things we know about a specific message
@@ -140,6 +141,20 @@ class NetlinkManager {
   // interface with index |interface_index|.
   virtual void UnsubscribeScanResultNotification(uint32_t interface_index);
 
+  // Sign up to be notified when there is MLME event.
+  // Only one handler can be registered per interface index.
+  // New handler will replace the registered handler if they are for the
+  // same interface index.
+  // NetlinkManager is not going to take ownership of this pointer, and that it
+  // is the caller's responsibility to make sure that the object exists for the
+  // duration of the subscription.
+  virtual void SubscribeMlmeEvent(uint32_t interface_index,
+                                  MlmeEventHandler* handler);
+
+  // Cancel the sign-up of receiving MLME event notification
+  // from interface with index |interface_index|.
+  virtual void UnsubscribeMlmeEvent(uint32_t interface_index);
+
   // Sign up to be notified when new scan results are available.
   // |handler| will be called when the kernel signals to wificond that a
   // scheduled scan has been completed on the given |interface_index|.
@@ -163,6 +178,7 @@ class NetlinkManager {
   bool DiscoverFamilyId();
   bool SendMessageInternal(const NL80211Packet& packet, int fd);
   void BroadcastHandler(std::unique_ptr<const NL80211Packet> packet);
+  void OnMlmeEvent(std::unique_ptr<const NL80211Packet> packet);
   void OnScanResultsReady(std::unique_ptr<const NL80211Packet> packet);
   void OnSchedScanResultsReady(std::unique_ptr<const NL80211Packet> packet);
 
@@ -193,6 +209,8 @@ class NetlinkManager {
   std::map<uint32_t, OnSchedScanResultsReadyHandler>
       on_sched_scan_result_ready_handler_;
 
+  std::map<uint32_t, MlmeEventHandler*> on_mlme_event_handler_;
+
   // Mapping from family name to family id, and group name to group id.
   std::map<std::string, MessageType> message_types_;
 
index 52ebcd4..8a76149 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <android-base/logging.h>
 
+#include "wificond/net/mlme_event_handler.h"
 #include "wificond/net/netlink_manager.h"
 #include "wificond/net/nl80211_packet.h"
 
@@ -354,5 +355,14 @@ bool NetlinkUtils::GetStationInfo(uint32_t interface_index,
   return true;
 }
 
+void NetlinkUtils::SubscribeMlmeEvent(uint32_t interface_index,
+                                      MlmeEventHandler* handler) {
+  netlink_manager_->SubscribeMlmeEvent(interface_index, handler);
+}
+
+void NetlinkUtils::UnsubscribeMlmeEvent(uint32_t interface_index) {
+  netlink_manager_->UnsubscribeMlmeEvent(interface_index);
+}
+
 }  // namespace wificond
 }  // namespace android
index 08337a5..02a813f 100644 (file)
@@ -79,6 +79,7 @@ struct StationInfo {
   // We will add them once we find them useful.
 };
 
+class MlmeEventHandler;
 class NetlinkManager;
 class NL80211Packet;
 
@@ -117,6 +118,20 @@ class NetlinkUtils {
                               const std::vector<uint8_t>& mac_address,
                               StationInfo* out_station_info);
 
+  // Sign up to be notified when there is MLME event.
+  // Only one handler can be registered per interface index.
+  // New handler will replace the registered handler if they are for the
+  // same interface index.
+  // NetlinkUtils is not going to take ownership of this pointer, and that it
+  // is the caller's responsibility to make sure that the object exists for the
+  // duration of the subscription.
+  virtual void SubscribeMlmeEvent(uint32_t interface_index,
+                                  MlmeEventHandler* handler);
+
+  // Cancel the sign-up of receiving MLME event notification
+  // from interface with index |interface_index|.
+  virtual void UnsubscribeMlmeEvent(uint32_t interface_index);
+
  private:
   bool ParseBandInfo(const NL80211Packet* const packet,
                      BandInfo* out_band_info);