OSDN Git Service

Monitor station events for hotspot mode
authorNingyuan Wang <nywang@google.com>
Wed, 8 Mar 2017 16:44:39 +0000 (08:44 -0800)
committerNingyuan Wang <nywang@google.com>
Thu, 16 Mar 2017 18:40:41 +0000 (11:40 -0700)
This enables NetlinkManager to monitor NL80211_CMD_NEW_STATION,
and NL80211_CMD_DEL_STATION.

This could be used for ApInterfaceImpl for monitoring associated
stations.

This also creates a new class LoggingUtils to avoid repeating code.
This also adds corresponding unit tests.

Bug: 36043922
Test: compile, unit tests, manual tests

Change-Id: I060062c0cd6250051da90658b229d786f287fad4

12 files changed:
Android.mk
ap_interface_impl.cpp
ap_interface_impl.h
logging_utils.cpp [new file with mode: 0644]
logging_utils.h [new file with mode: 0644]
net/netlink_manager.cpp
net/netlink_manager.h
net/netlink_utils.cpp
net/netlink_utils.h
scanning/scan_result.cpp
tests/ap_interface_impl_unittest.cpp
tests/mock_netlink_utils.h

index 7cd5daa..3579197 100644 (file)
@@ -52,6 +52,7 @@ LOCAL_SRC_FILES := \
     ap_interface_impl.cpp \
     client_interface_binder.cpp \
     client_interface_impl.cpp \
+    logging_utils.cpp \
     looper_backed_event_loop.cpp \
     rtt/rtt_controller_binder.cpp \
     rtt/rtt_controller_impl.cpp \
index 62fa2ae..44a30b3 100644 (file)
@@ -21,6 +21,7 @@
 #include "wificond/net/netlink_utils.h"
 
 #include "wificond/ap_interface_binder.h"
+#include "wificond/logging_utils.h"
 
 using android::net::wifi::IApInterface;
 using android::wifi_system::HostapdManager;
@@ -31,6 +32,8 @@ using std::vector;
 
 using EncryptionType = android::wifi_system::HostapdManager::EncryptionType;
 
+using namespace std::placeholders;
+
 namespace android {
 namespace wificond {
 
@@ -48,11 +51,18 @@ ApInterfaceImpl::ApInterfaceImpl(const string& interface_name,
   // This log keeps compiler happy.
   LOG(DEBUG) << "Created ap interface " << interface_name_
              << " with index " << interface_index_;
+
+  netlink_utils_->SubscribeStationEvent(
+      interface_index_,
+      std::bind(&ApInterfaceImpl::OnStationEvent,
+                this,
+                _1, _2));
 }
 
 ApInterfaceImpl::~ApInterfaceImpl() {
   binder_->NotifyImplDead();
   if_tool_->SetUpState(interface_name_.c_str(), false);
+  netlink_utils_->UnsubscribeStationEvent(interface_index_);
 }
 
 sp<IApInterface> ApInterfaceImpl::GetBinder() const {
@@ -104,5 +114,18 @@ bool ApInterfaceImpl::WriteHostapdConfig(const vector<uint8_t>& ssid,
   return hostapd_manager_->WriteHostapdConfig(config);
 }
 
+void ApInterfaceImpl::OnStationEvent(StationEvent event,
+                                     const vector<uint8_t>& mac_address) {
+  if (event == NEW_STATION) {
+    LOG(INFO) << "New station "
+              << LoggingUtils::GetMacString(mac_address)
+              << " associated with hotspot";
+  } else if (event == DEL_STATION) {
+    LOG(INFO) << "Station "
+              << LoggingUtils::GetMacString(mac_address)
+              << " disassociated from hotspot";
+  }
+}
+
 }  // namespace wificond
 }  // namespace android
index de6af5d..891a255 100644 (file)
@@ -24,6 +24,8 @@
 #include <wifi_system/hostapd_manager.h>
 #include <wifi_system/interface_tool.h>
 
+#include "wificond/net/netlink_manager.h"
+
 #include "android/net/wifi/IApInterface.h"
 
 namespace android {
@@ -66,6 +68,9 @@ class ApInterfaceImpl {
   wifi_system::HostapdManager* const hostapd_manager_;
   const android::sp<ApInterfaceBinder> binder_;
 
+  void OnStationEvent(StationEvent event,
+                      const std::vector<uint8_t>& mac_address);
+
   DISALLOW_COPY_AND_ASSIGN(ApInterfaceImpl);
 };
 
diff --git a/logging_utils.cpp b/logging_utils.cpp
new file mode 100644 (file)
index 0000000..852c608
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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/logging_utils.h"
+
+#include <iomanip>
+#include <vector>
+
+#include <android-base/macros.h>
+
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace android {
+namespace wificond {
+
+string LoggingUtils::GetMacString(const vector<uint8_t>& mac_address) {
+  stringstream ss;
+  for (const uint8_t& b : mac_address) {
+    ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(b);
+    if (&b != &mac_address.back()) {
+      ss << ":";
+    }
+  }
+  return ss.str();
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/logging_utils.h b/logging_utils.h
new file mode 100644 (file)
index 0000000..59a9f80
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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_LOGGING_UTILS_H_
+#define WIFICOND_LOGGING_UTILS_H_
+
+#include <vector>
+#include <sstream>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wificond {
+
+class LoggingUtils {
+ public:
+  LoggingUtils() = default;
+  static std::string GetMacString(const std::vector<uint8_t>& mac_address);
+
+ private:
+
+  DISALLOW_COPY_AND_ASSIGN(LoggingUtils);
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_LOGGING_UTILS_H_
index 193efce..74b156c 100644 (file)
@@ -502,6 +502,29 @@ void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
     OnRegChangeEvent(std::move(packet));
     return;
   }
+  // Station eventsFor AP mode.
+  if (command == NL80211_CMD_NEW_STATION ||
+      command == NL80211_CMD_DEL_STATION) {
+    uint32_t if_index;
+    if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
+      LOG(WARNING) << "Failed to get interface index from station event";
+      return;
+    }
+    const auto handler = on_station_event_handler_.find(if_index);
+    if (handler != on_station_event_handler_.end()) {
+      vector<uint8_t> mac_address;
+      if (!packet->GetAttributeValue(NL80211_ATTR_MAC, &mac_address)) {
+        LOG(WARNING) << "Failed to get mac address from station event";
+        return;
+      }
+      if (command == NL80211_CMD_NEW_STATION) {
+        handler->second(NEW_STATION, mac_address);
+      } else {
+        handler->second(DEL_STATION, mac_address);
+      }
+    }
+    return;
+  }
 }
 
 void NetlinkManager::OnRegChangeEvent(unique_ptr<const NL80211Packet> packet) {
@@ -540,7 +563,7 @@ void NetlinkManager::OnRegChangeEvent(unique_ptr<const NL80211Packet> packet) {
     return;
   }
 
-  auto handler = on_reg_domain_changed_handler_.find(wiphy_index);
+  const auto handler = on_reg_domain_changed_handler_.find(wiphy_index);
   if (handler == on_reg_domain_changed_handler_.end()) {
     LOG(DEBUG) << "No handler for country code changed event from wiphy"
                << "with index: " << wiphy_index;
@@ -556,7 +579,7 @@ void NetlinkManager::OnMlmeEvent(unique_ptr<const NL80211Packet> packet) {
     LOG(ERROR) << "Failed to get interface index from a MLME event message";
     return;
   }
-  auto handler = on_mlme_event_handler_.find(if_index);
+  const 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;
@@ -608,7 +631,7 @@ void NetlinkManager::OnSchedScanResultsReady(unique_ptr<const NL80211Packet> pac
     return;
   }
 
-  auto handler = on_sched_scan_result_ready_handler_.find(if_index);
+  const auto handler = on_sched_scan_result_ready_handler_.find(if_index);
   if (handler == on_sched_scan_result_ready_handler_.end()) {
     LOG(DEBUG) << "No handler for scheduled scan result notification from"
                << " interface with index: " << if_index;
@@ -629,7 +652,7 @@ void NetlinkManager::OnScanResultsReady(unique_ptr<const NL80211Packet> packet)
     aborted = true;
   }
 
-  auto handler = on_scan_result_ready_handler_.find(if_index);
+  const auto handler = on_scan_result_ready_handler_.find(if_index);
   if (handler == on_scan_result_ready_handler_.end()) {
     LOG(WARNING) << "No handler for scan result notification from interface"
                  << " with index: " << if_index;
@@ -662,6 +685,16 @@ void NetlinkManager::OnScanResultsReady(unique_ptr<const NL80211Packet> packet)
   handler->second(if_index, aborted, ssids, freqs);
 }
 
+void NetlinkManager::SubscribeStationEvent(
+    uint32_t interface_index,
+    OnStationEventHandler handler) {
+  on_station_event_handler_[interface_index] = handler;
+}
+
+void NetlinkManager::UnsubscribeStationEvent(uint32_t interface_index) {
+  on_station_event_handler_.erase(interface_index);
+}
+
 void NetlinkManager::SubscribeRegDomainChange(
     uint32_t wiphy_index,
     OnRegDomainChangedHandler handler) {
index 72b7d31..5d84d92 100644 (file)
@@ -84,6 +84,20 @@ typedef std::function<void(
 typedef std::function<void(
     std::string& country_code)> OnRegDomainChangedHandler;
 
+// Enum used for identifying the type of a station event.
+// This is used by function |OnStationEventHandler|.
+enum StationEvent {
+    NEW_STATION,
+    DEL_STATION
+};
+
+// This describes a type of function handling station events.
+// |event| specifies the type of this event.
+// |mac_address| is the station mac address associated with this event.
+typedef std::function<void(
+    StationEvent event,
+    const std::vector<uint8_t>& mac_address)> OnStationEventHandler;
+
 class NetlinkManager {
  public:
   explicit NetlinkManager(EventLoop* event_loop);
@@ -203,6 +217,16 @@ class NetlinkManager {
   // from wiphy with index |wiphy_index|.
   virtual void UnsubscribeRegDomainChange(uint32_t wiphy_index);
 
+  // Sign up to be notified when there is an station 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.
+  virtual void SubscribeStationEvent(uint32_t interface_index,
+                                     OnStationEventHandler handler);
+
+  // Cancel the sign-up of receiving station events.
+  virtual void UnsubscribeStationEvent(uint32_t interface_index);
+
  private:
   bool SetupSocket(android::base::unique_fd* netlink_fd);
   bool WatchSocket(android::base::unique_fd* netlink_fd);
@@ -248,6 +272,8 @@ class NetlinkManager {
   // regulatory domain change notifications.
   std::map<uint32_t, OnRegDomainChangedHandler> on_reg_domain_changed_handler_;
 
+  std::map<uint32_t, OnStationEventHandler> on_station_event_handler_;
+
   // Mapping from family name to family id, and group name to group id.
   std::map<std::string, MessageType> message_types_;
 
index 6a33d67..0fa0116 100644 (file)
@@ -400,5 +400,14 @@ void NetlinkUtils::UnsubscribeRegDomainChange(uint32_t wiphy_index) {
   netlink_manager_->UnsubscribeRegDomainChange(wiphy_index);
 }
 
+void NetlinkUtils::SubscribeStationEvent(uint32_t interface_index,
+                                         OnStationEventHandler handler) {
+  netlink_manager_->SubscribeStationEvent(interface_index, handler);
+}
+
+void NetlinkUtils::UnsubscribeStationEvent(uint32_t interface_index) {
+  netlink_manager_->UnsubscribeStationEvent(interface_index);
+}
+
 }  // namespace wificond
 }  // namespace android
index 048deb7..f8b9c0e 100644 (file)
@@ -193,6 +193,16 @@ class NetlinkUtils {
   // from wiphy with index |wiphy_index|.
   virtual void UnsubscribeRegDomainChange(uint32_t wiphy_index);
 
+  // Sign up to be notified when there is an station 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.
+  virtual void SubscribeStationEvent(uint32_t interface_index,
+                                     OnStationEventHandler handler);
+
+  // Cancel the sign-up of receiving station events.
+  virtual void UnsubscribeStationEvent(uint32_t interface_index);
+
  private:
   bool ParseBandInfo(const NL80211Packet* const packet,
                      BandInfo* out_band_info);
index c6f47c0..2307d49 100644 (file)
 
 #include "wificond/scanning/scan_result.h"
 
-#include <iomanip>
-#include <sstream>
-
 #include <android-base/logging.h>
 
+#include "wificond/logging_utils.h"
 #include "wificond/parcelable_utils.h"
 
 using android::status_t;
 using android::OK;
 using std::string;
-using std::stringstream;
 
 namespace com {
 namespace android {
@@ -86,16 +83,8 @@ void NativeScanResult::DebugLog() {
   string ssid_str(ssid.data(), ssid.data() + ssid.size());
   LOG(INFO) << "SSID: " << ssid_str;
 
-  stringstream ss;
-  string bssid_str;
-  for (uint8_t& b : bssid) {
-    ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(b);
-    if (&b != &bssid.back()) {
-      ss << ":";
-    }
-  }
-  bssid_str = ss.str();
-  LOG(INFO) << "BSSID: " << bssid_str;
+  LOG(INFO) << "BSSID: "
+            << ::android::wificond::LoggingUtils::GetMacString(bssid);
   LOG(INFO) << "FREQUENCY: " << frequency;
   LOG(INFO) << "SIGNAL: " << signal_mbm/100 << "dBm";
   LOG(INFO) << "TSF: " << tsf;
index 9f57d41..6092b63 100644 (file)
@@ -56,11 +56,19 @@ class ApInterfaceImplTest : public ::testing::Test {
   unique_ptr<NiceMock<MockNetlinkUtils>> netlink_utils_{
       new NiceMock<MockNetlinkUtils>(netlink_manager_.get())};
 
-  ApInterfaceImpl ap_interface_{kTestInterfaceName,
-                                kTestInterfaceIndex,
-                                netlink_utils_.get(),
-                                if_tool_.get(),
-                                hostapd_manager_.get()};
+  unique_ptr<ApInterfaceImpl> ap_interface_;
+
+  void SetUp() override {
+    EXPECT_CALL(*netlink_utils_,
+                SubscribeStationEvent(kTestInterfaceIndex, _));
+
+    ap_interface_.reset(new ApInterfaceImpl(
+        kTestInterfaceName,
+        kTestInterfaceIndex,
+        netlink_utils_.get(),
+        if_tool_.get(),
+        hostapd_manager_.get()));
+  }
 };  // class ApInterfaceImplTest
 
 }  // namespace
@@ -68,19 +76,19 @@ class ApInterfaceImplTest : public ::testing::Test {
 TEST_F(ApInterfaceImplTest, ShouldReportStartFailure) {
   EXPECT_CALL(*hostapd_manager_, StartHostapd())
       .WillOnce(Return(false));
-  EXPECT_FALSE(ap_interface_.StartHostapd());
+  EXPECT_FALSE(ap_interface_->StartHostapd());
 }
 
 TEST_F(ApInterfaceImplTest, ShouldReportStartSuccess) {
   EXPECT_CALL(*hostapd_manager_, StartHostapd())
       .WillOnce(Return(true));
-  EXPECT_TRUE(ap_interface_.StartHostapd());
+  EXPECT_TRUE(ap_interface_->StartHostapd());
 }
 
 TEST_F(ApInterfaceImplTest, ShouldReportStopFailure) {
   EXPECT_CALL(*hostapd_manager_, StopHostapd())
       .WillOnce(Return(false));
-  EXPECT_FALSE(ap_interface_.StopHostapd());
+  EXPECT_FALSE(ap_interface_->StopHostapd());
 }
 
 TEST_F(ApInterfaceImplTest, ShouldReportStopSuccess) {
@@ -91,7 +99,7 @@ TEST_F(ApInterfaceImplTest, ShouldReportStopSuccess) {
   EXPECT_CALL(*netlink_utils_, SetInterfaceMode(
       kTestInterfaceIndex,
       NetlinkUtils::STATION_MODE)).WillOnce(Return(true));
-  EXPECT_TRUE(ap_interface_.StopHostapd());
+  EXPECT_TRUE(ap_interface_->StopHostapd());
   testing::Mock::VerifyAndClearExpectations(if_tool_.get());
 }
 
@@ -99,7 +107,7 @@ TEST_F(ApInterfaceImplTest, ShouldRejectInvalidConfig) {
   EXPECT_CALL(*hostapd_manager_, CreateHostapdConfig(_, _, _, _, _, _))
       .WillOnce(Return(""));
   EXPECT_CALL(*hostapd_manager_, WriteHostapdConfig(_)).Times(0);
-  EXPECT_FALSE(ap_interface_.WriteHostapdConfig(
+  EXPECT_FALSE(ap_interface_->WriteHostapdConfig(
         vector<uint8_t>(),
         false,
         0,
index a8fd63c..1a0642e 100644 (file)
@@ -32,6 +32,7 @@ class MockNetlinkUtils : public NetlinkUtils {
   MOCK_METHOD1(GetWiphyIndex, bool(uint32_t* out_wiphy_index));
   MOCK_METHOD1(UnsubscribeMlmeEvent, void(uint32_t interface_index));
   MOCK_METHOD1(UnsubscribeRegDomainChange, void(uint32_t wiphy_index));
+  MOCK_METHOD1(UnsubscribeStationEvent, void(uint32_t interface_index));
   MOCK_METHOD2(SetInterfaceMode,
                bool(uint32_t interface_index, InterfaceMode mode));
   MOCK_METHOD2(SubscribeMlmeEvent,
@@ -40,6 +41,9 @@ class MockNetlinkUtils : public NetlinkUtils {
   MOCK_METHOD2(SubscribeRegDomainChange,
                void(uint32_t wiphy_index,
                     OnRegDomainChangedHandler handler));
+  MOCK_METHOD2(SubscribeStationEvent,
+               void(uint32_t interface_index,
+                    OnStationEventHandler handler));
 
   MOCK_METHOD2(GetInterfaces,
                bool(uint32_t wiphy_index,