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;
}
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, ®_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) {
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) {
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);
// 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);
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);
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_;
#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;
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
#include <android-base/macros.h>
+#include "wificond/net/netlink_manager.h"
+
namespace android {
namespace wificond {
// 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);
#include "wificond/server.h"
+#include <sstream>
+
#include <android-base/logging.h>
#include "wificond/net/netlink_utils.h"
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;
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 {
}
ap_interfaces_.clear();
+ netlink_utils_->UnsubscribeRegDomainChange(wiphy_index_);
+
if (!driver_tool_->UnloadDriver()) {
LOG(ERROR) << "Failed to unload WiFi driver!";
}
return false;
}
+ netlink_utils_->SubscribeRegDomainChange(
+ wiphy_index_,
+ std::bind(&Server::OnRegDomainChanged,
+ this,
+ _1));
+
if (!netlink_utils_->GetInterfaceInfo(wiphy_index_,
interface_name,
interface_index,
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_) {
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(
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,
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));
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());