OSDN Git Service

Monitor regulatory domain change
authorNingyuan Wang <nywang@google.com>
Fri, 2 Dec 2016 23:09:53 +0000 (15:09 -0800)
committerNingyuan Wang <nywang@google.com>
Thu, 16 Feb 2017 22:12:26 +0000 (14:12 -0800)
This adds the function of regulatory domain change monitoring
on wificond.

This also allows wificond to print the supported bands/channels
upon regulatory domain change.

This also adds the corresponding unit tests.

Bug: 35150708
Test: compile, unit tests, manual tests
Change-Id: Idbcf9ebf25f4e7be3b371ec3531b6b52303476e8

net/netlink_manager.cpp
net/netlink_manager.h
net/netlink_utils.cpp
net/netlink_utils.h
server.cpp
server.h
tests/mock_netlink_utils.h
tests/server_unittest.cpp

index 45ecc18..11d2cab 100644 (file)
@@ -216,9 +216,15 @@ bool NetlinkManager::Start() {
   if (!WatchSocket(&async_netlink_fd_)) {
     return false;
   }
+  // Subscribe kernel NL80211 broadcast of regulatory changes.
+  if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_REG)) {
+    return false;
+  }
+  // Subscribe kernel NL80211 broadcast of scanning events.
   if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_SCAN)) {
     return false;
   }
+  // Subscribe kernel NL80211 broadcast of MLME events.
   if (!SubscribeToEvents(NL80211_MULTICAST_GROUP_MLME)) {
     return false;
   }
@@ -490,6 +496,55 @@ void NetlinkManager::BroadcastHandler(unique_ptr<const NL80211Packet> packet) {
       OnMlmeEvent(std::move(packet));
      return;
   }
+  if (command == NL80211_CMD_REG_CHANGE) {
+    OnRegChangeEvent(std::move(packet));
+    return;
+  }
+}
+
+void NetlinkManager::OnRegChangeEvent(unique_ptr<const NL80211Packet> packet) {
+  uint32_t wiphy_index;
+  if (!packet->GetAttributeValue(NL80211_ATTR_WIPHY, &wiphy_index)) {
+    LOG(ERROR) << "Failed to get wiphy index from reg changed message";
+    return;
+  }
+
+  uint8_t reg_type;
+  if (!packet->GetAttributeValue(NL80211_ATTR_REG_TYPE, &reg_type)) {
+    LOG(ERROR) << "Failed to get NL80211_ATTR_REG_TYPE";
+  }
+
+  string country_code;
+  // NL80211_REGDOM_TYPE_COUNTRY means the regulatory domain set is one that
+  // pertains to a specific country
+  if (reg_type == NL80211_REGDOM_TYPE_COUNTRY) {
+    if (!packet->GetAttributeValue(NL80211_ATTR_REG_ALPHA2, &country_code)) {
+      LOG(ERROR) << "Failed to get NL80211_ATTR_REG_ALPHA2";
+      return;
+    }
+  } else if (reg_type == NL80211_REGDOM_TYPE_WORLD ||
+      reg_type == NL80211_REGDOM_TYPE_CUSTOM_WORLD ||
+      reg_type == NL80211_REGDOM_TYPE_INTERSECTION) {
+    // NL80211_REGDOM_TYPE_WORLD refers to the world regulartory domain.
+    // NL80211_REGDOM_TYPE_CUSTOM_WORLD refers to the driver specific world
+    // regulartory domain.
+    // NL80211_REGDOM_TYPE_INTERSECTION refers to an intersection between two
+    // regulatory domains:
+    // The previously set regulatory domain on the system and the last accepted
+    // regulatory domain request to be processed.
+    country_code = "";
+  } else {
+    LOG(ERROR) << "Unknown type of regulatory domain change: " << (int)reg_type;
+    return;
+  }
+
+  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;
+    return;
+  }
+  handler->second(country_code);
 }
 
 void NetlinkManager::OnMlmeEvent(unique_ptr<const NL80211Packet> packet) {
@@ -590,6 +645,16 @@ void NetlinkManager::OnScanResultsReady(unique_ptr<const NL80211Packet> packet)
   handler->second(if_index, aborted, ssids, freqs);
 }
 
+void NetlinkManager::SubscribeRegDomainChange(
+    uint32_t wiphy_index,
+    OnRegDomainChangedHandler handler) {
+  on_reg_domain_changed_handler_[wiphy_index] = handler;
+}
+
+void NetlinkManager::UnsubscribeRegDomainChange(uint32_t wiphy_index) {
+  on_reg_domain_changed_handler_.erase(wiphy_index);
+}
+
 void NetlinkManager::SubscribeScanResultNotification(
     uint32_t interface_index,
     OnScanResultsReadyHandler handler) {
index 9d29128..72b7d31 100644 (file)
@@ -73,6 +73,17 @@ typedef std::function<void(
     uint32_t interface_index,
     bool scan_stopped)> OnSchedScanResultsReadyHandler;
 
+// This describes a type of function handling regulatory domain change
+// notification.
+// If the regulatory domain set is one that pertains to a specific country,
+// |country_code| will be set accordingly.
+// If the regulatory domain set does not pertain to a specific country,
+// |country_code| will be an empty string. This could be a world regulatory
+// domain or a intersection regulatory domain.
+// See details in defination of |nl80211_reg_type| from nl80211.h.
+typedef std::function<void(
+    std::string& country_code)> OnRegDomainChangedHandler;
+
 class NetlinkManager {
  public:
   explicit NetlinkManager(EventLoop* event_loop);
@@ -181,6 +192,17 @@ class NetlinkManager {
   // interface with index |interface_index|.
   virtual void UnsubscribeSchedScanResultNotification(uint32_t interface_index);
 
+  // Sign up to be notified when there is an regulatory domain change.
+  // Only one handler can be registered per wiphy index.
+  // New handler will replace the registered handler if they are for the
+  // same wiphy index.
+  virtual void SubscribeRegDomainChange(uint32_t wiphy_index,
+                                        OnRegDomainChangedHandler handler);
+
+  // Cancel the sign-up of receiving regulatory domain change notification
+  // from wiphy with index |wiphy_index|.
+  virtual void UnsubscribeRegDomainChange(uint32_t wiphy_index);
+
  private:
   bool SetupSocket(android::base::unique_fd* netlink_fd);
   bool WatchSocket(android::base::unique_fd* netlink_fd);
@@ -188,6 +210,7 @@ class NetlinkManager {
   bool DiscoverFamilyId();
   bool SendMessageInternal(const NL80211Packet& packet, int fd);
   void BroadcastHandler(std::unique_ptr<const NL80211Packet> packet);
+  void OnRegChangeEvent(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);
@@ -221,6 +244,10 @@ class NetlinkManager {
 
   std::map<uint32_t, MlmeEventHandler*> on_mlme_event_handler_;
 
+  // A mapping from wiphy index to the handler registered to receive
+  // regulatory domain change notifications.
+  std::map<uint32_t, OnRegDomainChangedHandler> on_reg_domain_changed_handler_;
+
   // Mapping from family name to family id, and group name to group id.
   std::map<std::string, MessageType> message_types_;
 
index bcd369a..1b8d2a3 100644 (file)
@@ -25,7 +25,6 @@
 #include <android-base/logging.h>
 
 #include "wificond/net/mlme_event_handler.h"
-#include "wificond/net/netlink_manager.h"
 #include "wificond/net/nl80211_packet.h"
 
 using std::string;
@@ -372,5 +371,15 @@ void NetlinkUtils::UnsubscribeMlmeEvent(uint32_t interface_index) {
   netlink_manager_->UnsubscribeMlmeEvent(interface_index);
 }
 
+void NetlinkUtils::SubscribeRegDomainChange(
+    uint32_t wiphy_index,
+    OnRegDomainChangedHandler handler) {
+  netlink_manager_->SubscribeRegDomainChange(wiphy_index, handler);
+}
+
+void NetlinkUtils::UnsubscribeRegDomainChange(uint32_t wiphy_index) {
+  netlink_manager_->UnsubscribeRegDomainChange(wiphy_index);
+}
+
 }  // namespace wificond
 }  // namespace android
index 7fb2a2e..dbdb00a 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <android-base/macros.h>
 
+#include "wificond/net/netlink_manager.h"
+
 namespace android {
 namespace wificond {
 
@@ -151,6 +153,17 @@ class NetlinkUtils {
   // from interface with index |interface_index|.
   virtual void UnsubscribeMlmeEvent(uint32_t interface_index);
 
+  // Sign up to be notified when there is an regulatory domain change.
+  // Only one handler can be registered per wiphy index.
+  // New handler will replace the registered handler if they are for the
+  // same wiphy index.
+  virtual void SubscribeRegDomainChange(uint32_t wiphy_index,
+                                        OnRegDomainChangedHandler handler);
+
+  // Cancel the sign-up of receiving regulatory domain change notification
+  // from wiphy with index |wiphy_index|.
+  virtual void UnsubscribeRegDomainChange(uint32_t wiphy_index);
+
  private:
   bool ParseBandInfo(const NL80211Packet* const packet,
                      BandInfo* out_band_info);
index e12dd18..dca3002 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "wificond/server.h"
 
+#include <sstream>
+
 #include <android-base/logging.h>
 
 #include "wificond/net/netlink_utils.h"
@@ -24,9 +26,6 @@
 using android::binder::Status;
 using android::sp;
 using android::IBinder;
-using std::string;
-using std::vector;
-using std::unique_ptr;
 using android::net::wifi::IApInterface;
 using android::net::wifi::IClientInterface;
 using android::net::wifi::IInterfaceEventCallback;
@@ -38,6 +37,12 @@ using android::wifi_system::HostapdManager;
 using android::wifi_system::InterfaceTool;
 using android::wifi_system::SupplicantManager;
 
+using std::placeholders::_1;
+using std::string;
+using std::stringstream;
+using std::unique_ptr;
+using std::vector;
+
 namespace android {
 namespace wificond {
 
@@ -164,6 +169,8 @@ Status Server::tearDownInterfaces() {
   }
   ap_interfaces_.clear();
 
+  netlink_utils_->UnsubscribeRegDomainChange(wiphy_index_);
+
   if (!driver_tool_->UnloadDriver()) {
     LOG(ERROR) << "Failed to unload WiFi driver!";
   }
@@ -234,6 +241,12 @@ bool Server::SetupInterfaceForMode(int mode,
     return false;
   }
 
+  netlink_utils_->SubscribeRegDomainChange(
+          wiphy_index_,
+          std::bind(&Server::OnRegDomainChanged,
+          this,
+          _1));
+
   if (!netlink_utils_->GetInterfaceInfo(wiphy_index_,
                                         interface_name,
                                         interface_index,
@@ -253,6 +266,43 @@ bool Server::RefreshWiphyIndex() {
   return true;
 }
 
+void Server::OnRegDomainChanged(std::string& country_code) {
+  if (country_code.empty()) {
+    LOG(INFO) << "Regulatory domain changed";
+  } else {
+    LOG(INFO) << "Regulatory domain changed to country: " << country_code;
+  }
+  LogSupportedBands();
+}
+
+void Server::LogSupportedBands() {
+  BandInfo band_info;
+  ScanCapabilities scan_capabilities;
+  WiphyFeatures wiphy_features;
+  netlink_utils_->GetWiphyInfo(wiphy_index_,
+                               &band_info,
+                               &scan_capabilities,
+                               &wiphy_features);
+
+  stringstream ss;
+  for (unsigned int i = 0; i < band_info.band_2g.size(); i++) {
+    ss << " " << band_info.band_2g[i];
+  }
+  LOG(INFO) << "2.4Ghz frequencies:"<< ss.str();
+  ss.str("");
+
+  for (unsigned int i = 0; i < band_info.band_5g.size(); i++) {
+    ss << " " << band_info.band_5g[i];
+  }
+  LOG(INFO) << "5Ghz non-DFS frequencies:"<< ss.str();
+  ss.str("");
+
+  for (unsigned int i = 0; i < band_info.band_dfs.size(); i++) {
+    ss << " " << band_info.band_dfs[i];
+  }
+  LOG(INFO) << "5Ghz DFS frequencies:"<< ss.str();
+}
+
 void Server::BroadcastClientInterfaceReady(
     sp<IClientInterface> network_interface) {
   for (auto& it : interface_event_callbacks_) {
index 119ee2e..709e723 100644 (file)
--- a/server.h
+++ b/server.h
@@ -102,6 +102,8 @@ class Server : public android::net::wifi::BnWificond {
                              uint32_t* interface_index,
                              std::vector<uint8_t>* interface_mac_addr);
   bool RefreshWiphyIndex();
+  void LogSupportedBands();
+  void OnRegDomainChanged(std::string& country_code);
   void BroadcastClientInterfaceReady(
       android::sp<android::net::wifi::IClientInterface> network_interface);
   void BroadcastApInterfaceReady(
index 4d1e8c5..9eded29 100644 (file)
@@ -31,9 +31,14 @@ 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_METHOD2(SubscribeMlmeEvent,
                void(uint32_t interface_index,
                     MlmeEventHandler* handler));
+  MOCK_METHOD2(SubscribeRegDomainChange,
+               void(uint32_t wiphy_index,
+                    OnRegDomainChangedHandler handler));
+
   MOCK_METHOD4(GetInterfaceInfo,
                bool(uint32_t wiphy_index,
                     std::string* name,
index 4cf4ee5..ef21045 100644 (file)
@@ -103,6 +103,7 @@ TEST_F(ServerTest, CanSetUpApInterface) {
   EXPECT_CALL(*netlink_utils_, GetWiphyIndex(_))
       .InSequence(sequence)
       .WillOnce(Return(true));
+  EXPECT_CALL(*netlink_utils_, SubscribeRegDomainChange(_, _));
   EXPECT_CALL(*netlink_utils_, GetInterfaceInfo(_, _, _, _))
       .InSequence(sequence)
       .WillOnce(Return(true));
@@ -133,8 +134,10 @@ TEST_F(ServerTest, CanDestroyInterfaces) {
   EXPECT_CALL(*driver_tool_, UnloadDriver()).Times(0);
 
   EXPECT_TRUE(server_.createApInterface(&ap_if).isOk());
+
   // When we tear down the interface, we expect the driver to be unloaded.
   EXPECT_CALL(*driver_tool_, UnloadDriver()).Times(1).WillOnce(Return(true));
+  EXPECT_CALL(*netlink_utils_, UnsubscribeRegDomainChange(_));
   EXPECT_TRUE(server_.tearDownInterfaces().isOk());
   // After a teardown, we should be able to create another interface.
   EXPECT_TRUE(server_.createApInterface(&ap_if).isOk());